kil*_*s90 2 methods polymorphism vtable rust
以下是我对 Rust 方法如何工作的基本假设:
foo.method();
Run Code Online (Sandbox Code Playgroud)
其中method定义为method(&self)并且foo是 的一个实例Foo,与
Foo::method(&foo);
Run Code Online (Sandbox Code Playgroud)
我对 Trait Objects 的理解是一个有两个空指针的结构,一个指向数据,另一个指向函数指针(vtable)
接受 Trait 对象并调用该 Trait 对象上的方法的多态函数将编译为查看 Trait 对象中方法的偏移量并传入数据指针
但是如果该方法移动了实例呢?如果我错了,请纠正我,但要调用虚拟移动方法,该函数必须将存储在 Trait 对象中的实际数据推送到堆栈上,而不仅仅是数据指针。显然在编译时无法知道数据大小,那么这里发生了什么?这是 VLA 类型的情况,还是我误解了移动的工作原理?
答案很简单——在 trait 对象上调用自消费方法是不可能的。
关键词是对象安全。本质上,此属性结合了两个要求:
self通过某种间接方式使用;为了更详细地了解这一点,让我们实际尝试编写一些代码并询问编译器的意见。首先,只是试图定义特征:
trait Consumer {
fn consume(self);
}
Run Code Online (Sandbox Code Playgroud)
编译器已经不高兴了:
trait Consumer {
fn consume(self);
}
Run Code Online (Sandbox Code Playgroud)
好的,我们可以比编译器的建议更保守,并添加对 trait 的限制。然后,为 trait 对象创建添加一个存根:
trait Consumer where Self: Sized {
fn consume(self);
}
fn main() {
let _: Box<dyn Consumer> = todo!();
}
Run Code Online (Sandbox Code Playgroud)
现在,错误稍微复杂一些:
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/lib.rs:2:16
|
2 | fn consume(self);
| ^^^^ doesn't have a size known at compile-time
|
help: consider further restricting `Self`
|
2 | fn consume(self) where Self: Sized;
| ^^^^^^^^^^^^^^^^^
help: function arguments must have a statically known size, borrowed types always have a known size
|
2 | fn consume(&self);
| ^
Run Code Online (Sandbox Code Playgroud)
然而,有一个解决方法:没有必要限制整个特征——只是有问题的方法,正如我们从一开始就被告知的那样。移动where子句,如下:
trait Consumer {
fn consume(self) where Self: Sized;
}
Run Code Online (Sandbox Code Playgroud)
...使上面的代码编译。
现在,实际使用这个 trait 对象怎么样?例如,让我们为单位类型实现它,并使用它 from main:
trait Consumer {
fn consume(self) where Self: Sized;
}
impl Consumer for () {
fn consume(self) {}
}
fn main() {
let consumer: Box<dyn Consumer> = Box::new(());
consumer.consume();
}
Run Code Online (Sandbox Code Playgroud)
另一个编译器错误!
trait Consumer where Self: Sized {
fn consume(self);
}
fn main() {
let _: Box<dyn Consumer> = todo!();
}
Run Code Online (Sandbox Code Playgroud)
同样,我们对方法施加的限制禁止代码,如果它被编译将毫无意义。