在下面的代码中,我有一个简单的特征 A 和一个实现 A 的结构 Foo...
接下来,我定义了一个函数,它引用了一个特征对象。我从 main() 传入对具体 Foo 的引用。这工作正常并成功调用了特征中的方法。
trait A {
fn do_something(&self) {
println!("Doing something.");
}
}
struct Foo {}
impl A for Foo {}
fn main() {
let foo = Foo {};
make_do_something(&foo);
}
fn make_do_something(a: &dyn A) {
a.do_something();
}
Rust 编译器在这里做什么?具体的 Foo 引用是否自动强制转换为 Trait 对象引用?也就是说,是否在引用我的具体对象的堆上创建了一个新的特征对象?我在文档中找不到关于它如何工作的清晰描述。
具体的 Foo 引用是否自动强制转换为 Trait 对象引用?
是的,
&Foo
是强制到&dyn A
。 &Foo
包含一个指向 Foo
数据的指针(在您的示例中恰好是零字节长,但这没有区别); &dyn A
由该指针 和 指向从 impl A for Foo
生成的 vtable 的指针组成。
是在堆上创建的新特征对象
没有;特征对象需要额外的数据来引用它,而不是存储它。
将它与 Rust 中另一种主要的动态大小 (
!Sized
) 类型进行比较,切片:一个 &Foo
包含一个指向 Foo
的指针,而一个 &[Foo]
包含该指针 和一个长度 .它们可能指向相同的数据(你可以双向转换),但指针不同。
一般来说,指向一个
Sized
类型的值只需要指向数据的机器指针;指向 !Sized
类型的值需要额外的信息(称为“metadata”)。
每个指向动态大小类型的指针都是通过强制构造的,在这种情况下,编译器插入编译时已知的必要数据(特征对象的 vtable,或从指针数组强制转换的切片指针的长度) ,或者通过知道它正在为什么构造指针的库代码(例如,当
Vec<T>
产生&[T]
时)。