Rust结构体用dyn trait作字段报错,为何String可正常使用?
Rust中dyn Trait与String的大小差异问题
原始代码
trait Animal {} struct Sheep {} struct Cow {} // 为Sheep实现Animal trait impl Animal for Sheep {} // 为Cow实现Animal trait impl Animal for Cow {} struct Human { name: String, pet: dyn Animal, } impl Human { fn new(name: String, animal: dyn Animal) -> Self { Human { name, pet: animal } } fn say_my_name(&self) { println!("{}", self.name); } } fn main() { let sheep = Sheep {}; let human = Human::new(String::from("alex"), sheep); human.say_my_name(); }
编译错误信息
error[E0277]: the size for values of type `(dyn Animal + 'static)` cannot be known at compilation time --> src/main.rs:18:26 | 18 | fn new(name: String, animal: dyn Animal) -> Self { | ^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Animal + 'static)` help: you can use `impl Trait` as the argument type | 18 | fn new(name: String, animal: impl Animal) -> Self { | ~~~~ help: function arguments must have a statically known size, borrowed types always have a known size | 18 | fn new(name: String, animal: &dyn Animal) -> Self { | + error[E0308]: mismatched types --> src/main.rs:29:50 | 29 | let human = Human::new(String::from("alex"), sheep); | ---------- ^^^^^ expected `dyn Animal`, found `Sheep` | | | arguments to this function are incorrect | = note: expected trait object `(dyn Animal + 'static)` found struct `Sheep` note: associated function defined here --> src/main.rs:18:8 | 18 | fn new(name: String, animal: dyn Animal) -> Self { | ^^^ ------------------ error[E0277]: the size for values of type `(dyn Animal + 'static)` cannot be known at compilation time --> src/main.rs:29:9 | 29 | let human = Human::new(String::from("alex"), sheep); | ^^^^^ doesn't have a size known at compile-time | = help: within `Human`, the trait `Sized` is not implemented for `(dyn Animal + 'static)` note: required because it appears within the type `Human` --> src/main.rs:12:8 | 12 | struct Human { | ^^^^^ = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature error[E0277]: the size for values of type `(dyn Animal + 'static)` cannot be known at compilation time --> src/main.rs:29:50 | 29 | let human = Human::new(String::from("alex"), sheep); | ^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Animal + 'static)` = note: all function arguments must have a statically known size
疑问
编译器提示dyn Animal类型的大小在编译时无法确定,但Human结构体的name字段是String类型,同样涉及动态内容却能正常使用,这是为什么?
修复后的代码
trait Animal {} struct Sheep {} struct Cow {} // 为Sheep实现Animal trait impl Animal for Sheep {} // 为Cow实现Animal trait impl Animal for Cow {} struct Human { name: String, pet: Box<dyn Animal>, } impl Human { fn new(name: String, animal: Box<dyn Animal>) -> Self { Human { name, pet: animal } } fn say_my_name(&self) { println!("{}", self.name); } } fn main() { let sheep = Box::new(Sheep {}); let human = Human::new(String::from("alex"), sheep); human.say_my_name(); }
解答
核心差异:String是Sized类型,dyn Animal是非Sized类型
Rust中的String本质是一个固定大小的结构体,它包含三个字段:
- 指向堆内存的指针
- 字符串的当前长度
- 堆内存的容量
这三个字段的大小在编译时完全确定(64位系统下每个字段占8字节,总共24字节),所以String实现了Sized trait,完全符合Rust对结构体字段必须是Sized类型的要求(仅结构体最后一个字段可例外为非Sized,但有额外限制)。
而dyn Animal是 trait 对象,它代表任意实现了Animal trait的类型。不同类型(比如Sheep和Cow)的内存大小可能不同,编译器无法在编译时确定dyn Animal的具体大小,因此它属于非Sized类型,不能直接作为结构体字段或函数参数使用——Rust要求局部变量、函数参数、结构体字段(除特殊情况)必须在编译时确定大小,这样才能分配栈内存。
为什么Box<dyn Animal>能解决问题?
Box<T>是智能指针,它本身是固定大小的(64位系统下占8字节,就是一个堆内存地址)。它将dyn Animal这个非Sized类型存储在堆上,栈上仅保留指向堆内存的指针,这样整个Box<dyn Animal>的大小在编译时是确定的,满足了Rust的Sized要求。
总结:
String:栈上固定大小的结构体,指向堆上动态内容,本身是Sizeddyn Animal:无固定大小,是非Sized类型,不能直接使用Box<dyn Animal>:栈上固定大小的指针,指向堆上的trait对象,本身是Sized
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

