Why is the example in the book changed to generic and the prompt type does not match?

Why is the example in the book changed to generic and the prompt type does not match?

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

Complete Code:

lib.rs

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

// pub struct Screen {
//     pub components: Vec<Box<dyn Draw>>,
// }

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

// impl Screen {
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) {
        println!("draw Button width: {} height: {} label: {}",
                 self.width, self.height, self.label);
    }
}

pub struct SelectBox {
    pub width: u32,
    pub height: u32,
    pub options: Vec<String>,
}

impl Draw for SelectBox {
    fn draw(&self) {
        println!("draw SelectBox width: {} height: {} \
        options: {:?}",
                 self.width, self.height, self.options);
    }
}

main.rs

mod lib;
use lib::{Screen, Button, SelectBox};

fn main() {
    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();
}

The compilation error is as follows:

error[E0308]: mismatched types
  --> src\main.rs:17:17
   |
17 | /                 Button {
18 | |                     width: 50,
19 | |                     height: 10,
20 | |                     label: String::from("OK"),
21 | |                 }
   | |_________________^ expected struct `SelectBox`, found struct `Button`

Currently the compiler thinks you want Screen to be a Screen<Box<SelectBox>>, but you want it to be a Screen<Box<dyn Draw>>. Add a type annotation to help it out.

No way, still report an error.

let screen: Screen<Box<dyn Draw>> = 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"),
                }
            ),
        ]
    };
error[E0277]: the trait bound `Box<dyn Draw>: Draw` is not satisfied
 --> src\main.rs:5:17
  |
5 |     let screen: Screen<Box<dyn Draw>> = Screen {
  |                 ^^^^^^^^^^^^^^^^^^^^^ the trait `Draw` is not implemented for `Box<dyn Draw>`
  | 
 ::: src\lib.rs:9:22
  |
9 | pub struct Screen<T: Draw> {
  |                      ---- required by this bound in `Screen`


Sure, but this is a new, unrelated error. To fix this one, add this impl:

impl<T> Draw for Box<T>
where
    T: ?Sized + Draw,
{
    fn draw(&self) {
        let inner: &T = self;
        inner.draw();
    }
}
1 Like

@0XHyh To explain it some more, you changed the struct from

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

to

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

The place where the new version of the struct uses T, i.e. in Vec<T>, the old version of the struct used to have Vec<Box<dyn Draw>>, that is Box<dyn Draw> in place of T. Trait objects in Rust work by the fact that the type dyn Draw implements the trait Draw. But the boxed type Box<dyn Draw> doesn’t implement Draw without adding a manual implementation. You can still call methods on a Box<dyn Draw> due to dereferencing, i.e. a borrow &Box<dyn Draw> converts/dereferences to a borrow &dyn Draw which can then be passed to a &self method of dyn Draw.

Another approach that would work without the addition of a impl<T: Draw> Draw for Box<T> implementation is to make the struct use Vec<Box<T>> (and using T: ?Sized) in which case T == dyn Draw would fit right in as a special case. That would however come with some overhead as it still requires boxing everything in every use case. So I think in practical situations where you want to add generics to a struct that used to use Box<dyn Trait>, it’s more reasonably to just replace the whole box with a generic and add an implementation for Box if you haven’t already. Such implementations are really common for standard-library traits, too, e.g. for Iterator, Fn, io::Read, Future, etc… (you can ignore the A: Allocator stuff and the second argument to Box in these docs).

1 Like

You are so good, you answer many questions.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.