答案很简单——不可能在 trait 对象上调用自消费方法。
关键词是object safety。本质上,这个属性结合了两个要求:
- 每个方法都必须通过某种间接方式与
self 一起使用;
- 每个方法都必须通过原始类型完全可识别(即没有泛型)。
为了更详细地了解这一点,让我们实际尝试编写一些代码并询问编译器的意见。首先,只是尝试定义特征:
trait Consumer {
fn consume(self);
}
编译器已经不高兴了:
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);
| ^
好的,我们可以比编译器的建议更保守,并添加对 trait 的限制。然后,为 trait 对象创建添加一个存根:
trait Consumer where Self: Sized {
fn consume(self);
}
fn main() {
let _: Box<dyn Consumer> = todo!();
}
现在,错误稍微复杂了一点:
error[E0038]: the trait `Consumer` cannot be made into an object
--> src/main.rs:6:12
|
6 | let _: Box<dyn Consumer> = todo!();
| ^^^^^^^^^^^^^^^^^ `Consumer` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/main.rs:1:28
|
1 | trait Consumer where Self: Sized {
| -------- ^^^^^ ...because it requires `Self: Sized`
| |
| this trait cannot be made into an object...
但是,有一个解决方法:没有必要限制整个特征 - 只是有问题的方法,正如我们从一开始就被告知的那样。移动where 子句,如下:
trait Consumer {
fn consume(self) where Self: Sized;
}
...使上面的代码编译。
现在,实际上使用这个特征对象怎么样?让我们实现它,例如,对于单元类型,并从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();
}
另一个编译器错误!
error: the `consume` method cannot be invoked on a trait object
--> src/main.rs:11:14
|
2 | fn consume(self) where Self: Sized;
| ----- this has a `Sized` requirement
...
11 | consumer.consume();
| ^^^^^^^
同样,我们对方法的限制禁止了编译后没有意义的代码。