0

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.

piegames
  • 975
  • 12
  • 31
  • 2
    Does this answer your question? [How do I create a heterogeneous collection of objects?](https://stackoverflow.com/questions/27957103/how-do-i-create-a-heterogeneous-collection-of-objects) – trent Apr 12 '20 at 00:42
  • tl;dr - you can use references instead of `Box`, or you can use an enum. Also, [Use Trait as Vec Type](https://stackoverflow.com/q/46065104/3650362) might help some. – trent Apr 12 '20 at 00:43
  • @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: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f0ed475587e487f47e1b717fe518a24d 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 Apr 12 '20 at 10:08
  • @trentcl The solutions you propose all boil down to some form of dynamic dispatch, which I don't really want at that place. – piegames Apr 12 '20 at 10:10
  • *I want to to create a `Vec>` 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. – trent Apr 12 '20 at 12:27
  • @trentcl These are two different perspectives on the same problem. One is a `Struct>`, the other one is `Struct`. The use of `Vec` is more an example, the problem holds true for any other custom struct type. – piegames Apr 12 '20 at 12:33
  • By _"where each container may have a different trait implementation"_, do you mean that the container is a `TraitContainer`, or the collection of containers? For dynamic dispatching to only take place in the outer scope, the compiler needs at least to resolve all ambiguity there. – E_net4 Apr 13 '20 at 08:36

1 Answers1

0

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.

piegames
  • 975
  • 12
  • 31
  • 1
    This doesn't appear to answer the original question because `TraitContainer` is still subject to the same limitations as bare `dyn MyTrait` -- e.g., it can't be put in a `Vec`. Would it be possible to edit the question to make it more clear that this is an answer to it? – trent Apr 15 '20 at 15:13