Got the problem to run example in "ch17-02-trait-objects"

Hi there, I'm following the official book tutorial to go through all Rust concept, but I'm stopped on the topic of

Using Trait Objects That Allow for Values of Different Types

I put all the code together below and try to run and have a look:

pub trait Draw {
    fn draw(&self);
}

pub struct Screen<T: Draw> {
    pub components: Vec<T>,
}

impl<T> Screen<T>
where
    T: Draw,
{
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

pub struct Button {
    pub width: u32,
    pub height: u32,
    pub label: String,
}

impl Draw for Button {
    fn draw(&self) {
        // code to actually draw a button
    }
}

fn main() {
    struct SelectBox {
        width: u32,
        height: u32,
        options: Vec<String>,
    }

    impl Draw for SelectBox {
        fn draw(&self) {
            // code to actually draw a select box
        }
    }

    let screen = Screen {
        components: vec![
            Box::new(SelectBox {
                width: 75,
                height: 10,
                options: vec![
                    String::from("Yes"),
                    String::from("Maybe"),
                    String::from("No"),
                ],
            }),
            Box::new(Button {
                width: 50,
                height: 10,
                label: String::from("OK"),
            }),
        ],
    };

    screen.run();
}

But I can't compile it, and the error is quite confusing me, as I thought the official example should compile and run:

   --> src/main.rs:210:22
    |
210 |               Box::new(Button {
    |  ______________________^
211 | |                 width: 50,
212 | |                 height: 10,
213 | |                 label: String::from("OK"),
214 | |             }),
    | |_____________^ expected struct `main::SelectBox`, found struct `Button`

error[E0277]: the trait bound `std::boxed::Box<main::SelectBox>: Draw` is not satisfied
   --> src/main.rs:199:18
    |
159 | pub struct Screen<T: Draw> {
    | -------------------------- required by `Screen`
...
199 |     let screen = Screen {
    |                  ^^^^^^ the trait `Draw` is not implemented for `std::boxed::Box<main::SelectB
ox>`

error[E0599]: no method named `run` found for struct `Screen<std::boxed::Box<main::SelectBox>>` in t
he current scope
   --> src/main.rs:218:12
    |
159 | pub struct Screen<T: Draw> {
    | -------------------------- method `run` not found for this
...
218 |     screen.run();
    |            ^^^ method not found in `Screen<std::boxed::Box<main::SelectBox>>`
    |
    = note: the method `run` exists but the following trait bounds were not satisfied:
            `std::boxed::Box<main::SelectBox> : Draw`

error: aborting due to 3 previous errors

How to solve it actually? plz :slight_smile:

In the documentation, Screen contains not a Vec<T>, but a Vec<Box<dyn Draw>>, and that's the difference which causes an error.

There are, in fact, three problems arising from it. First, Vec<T> is a homogeneous vector. It means that, when you create your Screen struct, the following logical steps are performed:

  • Rust looks at the first element in the vector and finds it to be Box<SelectBox>.
  • Rust decides that this will be the T in vector definition, i.e. the vector is in fact Vec<Box<SelectBox>>.
  • Rust looks at the second element, finds it to be Box<Button> and fails with the "mismatched types" error.

The second problem is due to the fact that T: Trait does not enforce Box<T>: Trait, in particular, Box<SelectBox> does not implement Draw. So Screen<Box<SelectBox>> is invalid, since Screen<T> requires T: Draw.

And the third error is the consequence of the second one: you've implemented run on Screen<T: Draw>, so it errors when trying to run the method on Screen<Box<SelectBox>> (for this error, it's irrelevant that this Screen is itself invalid - it looks only on implementation).

With the change in Screen, the code runs well - playground.

1 Like

Thanks @Cerber-Ursi, After replace the Vec<Box<dyn Draw>>, it works fine for me. And your explanation is very clear as well. :+1:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.