0
votes

Given the following setup:

trait MyTrait {}

struct TraitContainer<T: MyTrait> {
  inner: T
}

I want to to create a Vec<TraitContainer<_>> where each container may have a different trait implementation. The naive way would be to remove the generic param and replace it with Vec<TraitContainer<Box<dyn MyTrait>>>. But I feel like I shouldn't need to. I want to do something like Vec<Box<dyn TraitContainer<MyTrait>>>, and it feels like it should be possible, but I don't really know how to make it work.

Full playground setup


To put my idea in another context: If I have a Vec<Box<dyn MyTrait>>, every time I want to access an object, it will do dynamic dispatch to find the correct implementation of the trait. But if I know that all items of my Vec will have the same type (I just don't know the exact one, only that they implement some trait), I should be able to do Box<dyn Vec<MyTrait>>. That way I still have dynamic dispatch, but moved to the outermost nesting level.

1
tl;dr - you can use references instead of Box, or you can use an enum. Also, Use Trait as Vec Type might help some.trentcl
@trentcl Yes and know. Yes, I can still add a new trait wrapper for my struct and use dynamic dispatch on it. But it will be pretty ugly and un-ergonomic: play.rust-lang.org/… It gets even worse when I add methods because they will be needlessle duplicated in both the trait and the struct. I hope there is another solution.piegames
@trentcl The solutions you propose all boil down to some form of dynamic dispatch, which I don't really want at that place.piegames
I want to to create a Vec<TraitContainer<_>> where each container may have a different trait implementation is in contradiction with I know that all items of my Vec will have the same type. If they have the same type, they can't have different implementations.trentcl

1 Answers

0
votes

Turns out it is pretty simple: The TraitContainer must be marked as unsized:

trait MyTrait {}

struct Foo;

impl MyTrait for Foo {}

struct TraitContainer<T: MyTrait + ?Sized>(T); // <--- "+ ?Sized"

impl MyTrait for Box<dyn MyTrait> {
}

fn main() {
    let works: TraitContainer<Foo> = TraitContainer(Foo);
    let still_works: TraitContainer<Box<dyn MyTrait>> = TraitContainer(Box::new(Foo));
    let yay: Box<TraitContainer<dyn MyTrait>> = Box::new(TraitContainer(Foo));
}

A caveat is that this doesn't work with the other example, because Vec<T> requires T: Sized. So Box<Vec<dyn MyTrait>> won't compile.