Storing elements in a Vec where each element implements multiple dyn Traits

I have types for which I implement different traits. I'd like to store references/Boxes to objects that implement common set of traits.

For example (not my use case) I'd like a vector where each element implements Clone, Display and PartialEq. Pseudo-code:

type Element = Clone + Display + PartialEq;

let v: Vec<&Element> = vec![];

I think I understand why I can't specify multiple traits like that (fat pointer only points to a single trait impl).

What are my options in Rust? I could have a helper that creates e.g. tuples with each index representing one & dyn trait but perhaps there's a better way.

You can create a helper trait that is equivalent to those three traits.

1 Like

Like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=3718782b2c5f4c37ea064ef4898ee71f

Unfortunately, PartialEq and Clone are not object safe and can't be used like this. Not sure how to solve this, though (and I actualy don't understand why... rust complains about them requiring 'Sized', but I don't see why that would be the case, seeing all their methods use &self ...)

1 Like

The Clone trait is not object safe because it returns Self. As for PartialEq, note that although String and i32 both implement PartialEq, you can't compare a String to an i32 — both sides must be the same type, and if you had a Box<dyn PartialEq>, there would be no way to know if they had the same type.

4 Likes

Thanks @alice and @KillTheMule. PartialEq was just my poor example.

Is this what you meant? Do I have to impl All for $type {} or is there some kind default impl?

// ...

pub trait All: Foo + Bar {}

impl All for Struct1 {}
impl All for Struct2 {}

fn main() {
    let v: Vec<&dyn All> = vec![&Struct1 {}, &Struct2 {}];
    for x in v {
        x.foo();
        x.bar();
    }
}

Full code in Playground

You can use a blanket impl.

impl<T> All for T
where
    T: Foo,
    T: Bar,
{}
2 Likes

Ah, "blanket" is the right word, should've realised that, thank you!

From best practices point of view, is there any advantage/disadvantage of using

impl<T> All for T where T: Foo + Bar {}

instead of

impl<T> All for T
where
    T: Foo,
    T: Bar,
{}

To my limited knowledge perhaps if Foo or Bar had associated types the latter would be much more readable.

Generally, if you want it on one line, you should use this syntax:

impl<T: Foo + Bar> All for T {}

When where bounds are in play, you should indent it over several lines as I did.

Thanks, makes sense. As for formatting I just follow rustfmt.

TL;DR

trait All: Foo + Bar {}

impl<T: Foo + Bar> All for T {}

fn main() {
    let v: Vec<&dyn All> = vec![&Struct1 {}, &Struct2 {}];
}

Playground

1 Like

Note that Vec<&dyn All> is nearly useless for anything but compile-time constants, and you probably want a box instead.

Similar discussion in related thread: Have supertrait upcasting coercions finally landed in stable?

1 Like

Not sure I understand -- my use case is borrowing instances that are allocated somewhere (doesn't matter whether on stack or heap) and construct Vec<&dyn All> for some of them and then operate on that collection. For this purpose there doesn't seem to be any compile-time constant limitation unless I misunderstood what you meant.

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.