You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跟随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();
}
错误原因与修复方案

核心问题

  1. 解引用trait对象无效:&dyn Draw是trait对象引用,底层类型的大小在编译期无法确定(dyn Draw不实现Sized),没法直接解引用后用Box::new包裹——Box::new需要明确知道要分配的内存大小。
  2. 遍历方式错误:for d in self.components会试图把向量里的Box<dyn Draw>移走,但self是不可变引用,不允许这种移动操作。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 05:03:23