在 Rust 中,为什么特征对象通常通过引用(&dyn 特征)或智能指针(如 Box)来使用
它们是通过引用和智能指针处理的,因为否则它们将无法编译:
trait Foo {}
struct Bar;
impl Foo for Bar {}
fn main() {
let f1: dyn Foo = Bar;
let f2: dyn Foo = Bar as dyn Foo;
}
error[E0308]: mismatched types
--> src/main.rs:6:23
|
6 | let f1: dyn Foo = Bar;
| ------- ^^^ expected `dyn Foo`, found `Bar`
| |
| expected due to this
|
= note: expected trait object `dyn Foo`
found struct `Bar`
= help: `Bar` implements `Foo` so you could box the found value and coerce it to the trait object `Box<dyn Foo>`, you will have to change the expected type as well
error[E0620]: cast to unsized type: `Bar` as `dyn Foo`
--> src/main.rs:7:23
|
7 | let f2: dyn Foo = Bar as dyn Foo;
| ^^^^^^^^^^^^^^
|
help: consider using a box or reference as appropriate
--> src/main.rs:7:23
|
7 | let f2: dyn Foo = Bar as dyn Foo;
| ^^^
error[E0277]: the size for values of type `dyn Foo` cannot be known at compilation time
--> src/main.rs:6:9
|
6 | let f1: dyn Foo = Bar;
| ^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn Foo`
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
help: consider borrowing here
|
6 | let f1: &dyn Foo = Bar;
| +
A
dyn Trait
是一种动态类型,表示实现该 Trait
的任何潜在具体类型。由于它代表各种可能不同大小的类型,因此它本身没有固定的大小。这称为 动态调整大小的类型 (DST)。
因为它们没有固定的大小,所以我们可以放置它们的位置受到限制。特别是 DST 不能存储在变量中;如果存储在结构中,它会使该结构成为 DST,因此也不能存储在变量中。有一个 unsized locals RFC 可能在未来使其成为可能,但现在它的命运尚不清楚。
旁注:您仍然可以将变量引用为动态特征,但这是因为变量仍然是已知的具体类型,即使特征对象动态访问它:
fn main() {
let b = Bar;
let f: &dyn Foo = &b;
}
因此,为了使用 DST,需要一些间接的方法;该值必须存储在其他地方,并且只能通过引用或智能指针(例如
Box
)来引用(Rc
,Arc
,也可以)。
技术上无关,但无论如何值得一提:对特征对象的引用和智能指针是“胖”的。这意味着它们实际上存储两个指针:一个指向数据,另一个指向映射到与该特征实现关联的特征函数的 v 表。请参阅什么是“胖指针”?了解更多信息。