跟随Rust书籍示例用dyn Trait遇E0277:如何向向量推入dyn trait?
问题描述
我实现了一个和Rust文档里Screen、Draw trait类似的dyn trait示例,但没有就地初始化向量,而是通过register函数向向量添加组件时,遇到错误:the trait Sized is not implemented for dyn Draw,不知道怎么修复。相关代码如下:
pub trait Draw { fn draw(&self); } pub struct Screen { pub components: Vec<Box<dyn Draw>>, } impl Screen { fn new() -> Self { Screen { components: Vec::new(), } } fn register(&mut self, w: &dyn Draw) { self.components.push(Box::new(*w)); } fn show(&self) { for d in self.components { d.draw() } } } struct TextBox { txt: String, } impl TextBox { fn new(t: &str) -> Self { TextBox { txt: t.to_string() } } } struct Button { label: String, } impl Button { fn new(l: &str) -> Self { Button { label: l.to_string(), } } } impl Draw for TextBox { fn draw(&self) { println!("{}", self.txt.as_str()) } } impl Draw for Button { fn draw(&self) { println!("{}", self.label.as_str()) } } fn main() { let s = Screen::new(); let b = Button::new("Button1"); let t = TextBox::new("Some text"); s.register(&b as &dyn Draw); s.register(&t as &dyn Draw); s.show(); }
错误原因与修复方案
核心问题
- 解引用trait对象无效:
&dyn Draw是trait对象引用,底层类型的大小在编译期无法确定(dyn Draw不实现Sized),没法直接解引用后用Box::new包裹——Box::new需要明确知道要分配的内存大小。 - 遍历方式错误:
for d in self.components会试图把向量里的Box<dyn Draw>移走,但self是不可变引用,不允许这种移动操作。 - Screen绑定不可变:
let s = Screen::new();是不可变绑定,调用register需要修改内部向量,必须用可变绑定。
修复后的完整代码
pub trait Draw { fn draw(&self); } pub struct Screen { pub components: Vec<Box<dyn Draw>>, } impl Screen { fn new() -> Self { Screen { components: Vec::new(), } } // 方案1:直接接收Box<dyn Draw>存入 fn register_box(&mut self, w: Box<dyn Draw>) { self.components.push(w); } // 方案2:接收任意实现Draw的类型,自动装箱(更方便) fn register<T: Draw + 'static>(&mut self, w: T) { self.components.push(Box::new(w)); } fn show(&self) { // 遍历引用,避免移动向量元素 for d in &self.components { d.draw() } } } struct TextBox { txt: String, } impl TextBox { fn new(t: &str) -> Self { TextBox { txt: t.to_string() } } } struct Button { label: String, } impl Button { fn new(l: &str) -> Self { Button { label: l.to_string(), } } } impl Draw for TextBox { fn draw(&self) { println!("{}", self.txt.as_str()) } } impl Draw for Button { fn draw(&self) { println!("{}", self.label.as_str()) } } fn main() { // 改为可变绑定,允许修改内部组件 let mut s = Screen::new(); let b = Button::new("Button1"); let t = TextBox::new("Some text"); // 使用泛型版register,自动完成装箱 s.register(b); s.register(t); s.show(); // 也可以手动装箱后用register_box添加 // s.register_box(Box::new(Button::new("Button2"))); // s.register_box(Box::new(TextBox::new("Another text"))); }
关键修复说明
register函数优化:提供两种实现,泛型版本无需手动装箱,直接传入实现Draw的类型即可;Box版本适合已经持有Box<dyn Draw>的场景。- 遍历逻辑调整:用
&self.components遍历元素引用,既可以调用draw方法,又不会破坏向量的所有权。 - Screen绑定改为可变:只有可变绑定才能调用修改内部状态的
register方法。
内容的提问来源于stack exchange,提问作者Zhou Lebo
相关产品推荐
相关产品推荐

