In rust, how can struct be used to pass parameters after implementing multiple traits

trait TraitA {
    fn test_1(&self);
}
trait TraitB {
    fn test_2(&self);
}

trait TraitAA {
    fn test_3(&self);
}
trait TraitBB {
    fn test_4(&self);
}
trait TraitCC {
    fn test_5(&self);
}

struct Book;
impl TraitA for Book {
    fn test_1(&self) {}
}
impl TraitB for Book {
    fn test_2(&self) {}
}

struct Docs1;
impl TraitAA for Docs1 {
    fn test_3(&self) {}
}
impl TraitBB for Docs1 {
    fn test_4(&self) {}
}
impl TraitCC for Docs1 {
    fn test_5(&self) {}
}

struct Docs2;
impl TraitAA for Docs2 {
    fn test_3(&self) {}
}
impl TraitBB for Docs2 {
    fn test_4(&self) {}
}
impl TraitCC for Docs2 {
    fn test_5(&self) {}
}

fn test_fn<T: TraitA + TraitB, U: TraitAA + TraitBB + TraitCC>(x: T, y: Vec<Box<U>>) {
    x.test_1();
    x.test_2();

    for item in y {
        item.test_3();
        item.test_4();
        item.test_5();
    }
}

Call as follows,yy parameter error: mismatched type expected Docs1, found Docs2, but my Docs1 and Docs2 both implement the same trait, why do I report this error

let xx = Book;
let yy = vec![Box::new(Docs1), Box::new(Docs2)];
test_fn(xx, yy);

How do you solve this?

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

This looks like an attempt to write some other language in Rust. Mistakes like 6a, 6c, 11, maybe 12 from the well-known list.

And the whole code just screms XYProblem at us: it really looks as if you try to kick the can down the road and postpone certain decisions for as long as possible… and Rust hates that, it's just not in Rust's nature.

There are certainly exist a way to do that, because it's needed, sometimes, but that's not easy. And certainly now what you are supposed to do in the beginning.

It would be nice to know just why you feel the need to use factory factory factory approach, but names like test_5 and Docs2 don't, really, give us many hints as to what is happening.


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

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>,
}
impl Screen {
    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
        println!("-------Button-Draw-------")
    }
}
struct SelectBox {
    width: u32,
    height: u32,
    options: Vec<String>,
}

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

The above is the official example, after testing completely no problem, now add requirements, that is, each component must implement Draw and DrawTwo can be used, the code is changed to the following, it will report an error such as the theme error:

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

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

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
        println!("-------Button-Draw-------")
    }
}
impl DrawTwo for Button {
    fn draw_two(&self) {
        // code to actually draw a button
        println!("-------Button-DrawTwo-------")
    }
}

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

impl Draw for SelectBox {
    fn draw(&self) {
        // code to actually draw a select box
        println!("-------SelectBox-Draw-------")
    }
}
impl DrawTwo for SelectBox {
    fn draw_two(&self) {
        // code to actually draw a button
        println!("-------SelectBox-DrawTwo-------")
    }
}

The call will report an error,


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();

How can a need like this be realized

Meaning, can't you use this line of thinking to accomplish such a requirement

The basic idea is to erase types via dyn Trait or dyn Trait1 + Trait2 + ....

So it seems you didn't get the point of trait object.

From the book, Screen is a concrete type without any type parameter:

But you still want it to be parameterized and thus try to pass distinct types to Screen.

In this case, the solutions in your question are

  • Option1: make T be dyn Draw + DrawTwo, which requires T to be ?Sized, and explicitly tell rustc you want Box<dyn ...> type, like Box::new(SelectBox {...}) as Box<dyn ...> or let screen: Box<dyn ...>
  • Option2: pub components: Vec<Box<dyn Draw + DrawTwo>> in Screen without generics.
  • Potential option: use an enum as a limited set of types
1 Like

Although I made it work, I don't think it is a good approach.

1 Like

What are the implementation recommendations for requirements like this

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

Merged together, as follows:

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

Everyone includes a lot of ha, rust just started soon, as I am a little white in the humble to ask for advice Ha

To answer this directly: Traits are not about subtyping. Implementing a trait doesn't change the type of the implementor.

Rust doesn't have subtypes in the sense that you're used to.

You can type-erased different base types into a dyn Trait, but that's not the same as subtyping.

Collect all the abilities you need into one trait, either by putting all the methods in one trait...

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

...or by using supertraits like @chung demonstrated.

1 Like

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.