Using Trait Objects That Allow for Values of Different Types

Hi, I'm looking to that documentation: Using Trait Objects That Allow for Values of Different Types - The Rust Programming Language

I wrote all codes like that but I'm getting errors:

trait Draw {
    fn draw(&self);
}

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

impl Draw for Button {
    fn draw(&self) {
        println!(
            "Button drawing, w: {}, h: {}, l: {}",
            self.width, self.height, self.label
        );
    }
}

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

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

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

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


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: 75,
            height: 20,
            label: String::from("OK"),
        })
      ]
    };

    screen.run();
}

I'm getting that error:

error[E0308]: mismatched types
  --> src/main.rs:64:18
   |
64 |           Box::new(Button {
   |  _________--------_^
   | |         |
   | |         arguments to this function are incorrect
65 | |             width: 75,
66 | |             height: 20,
67 | |             label: String::from("OK"),
68 | |         })
   | |_________^ expected struct `SelectBox`, found struct `Button`
   |
note: associated function defined here

For more information about this error, try `rustc --explain E0308`.
error: could not compile `my-project` due to previous error
exit status 101

I think it must work but I don't understand why I'm getting this error. What do you suggest? Thanks.

How does Screen know what T to use? Type inference looks at the first element of the Vec and thinks you are creating a Vec<Box<SelectBox>>, and the second element has the type Box<Button>, which causes a compile error.

You should be able to make type inference coerce the T to a Box<dyn Draw> by doing let screen: Screen<Box<dyn Draw>> = ....

1 Like

You will also probably need to update your bounds to T: ?Sized + Draw.

When I change the line to let screen: Screen<Box<dyn Draw>> = Screen { then I'm getting these errors:

60 |     let screen: Screen<Box<dyn Draw>> = Screen {
   |                 ^^^^^^^^^^^^^^^^^^^^^ the trait `Draw` is not
         implemented for `Box<dyn Draw>`

I think there is a bigger problem, the documentation is wrong or lack.

Oh hang on, their Screen type uses only trait objects, whereas your version is generic over some T: Draw.

Change your Screen type to look like their version:

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

The problem is that you are looking at the wrong part of the documentation. You have used the definition of Screen from 17-6 that uses generics, rather than the one from 17-4 and 17-5 that uses trait bounds.

It is saying that in 17-6:

Listing 17-6: An alternate implementation of the Screen struct and its run method using generics and trait bounds

What means is that?

What is shown there is how a Screen struct might be implemented using the generics discussed in Chapter 10 of the book, to make the point that it works differently to the trait object version in Listings 17-4 and 17-5. In particular, as the text below Listing 17-6 points out, the vector can only contain things of one type that implements Draw (which is the problem you experienced). With a trait object (dyn Draw), you have something that can contain values of any type that implements Draw.

Rust has this silly thing where Box<dyn Trait> does not implement the Trait itself. You actually need to write impl Trait for Box<dyn Trait> { <boring obvious boilerplate> } yourself to forward calls from the Box to the dyn Trait object.

1 Like

Thanks, I see. Unfortunatelly every language have silly features.

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.