1

(This is my second try to track down my exact problem. See the edit history)

I have a simple generic Trait and two different implementations:

pub trait MyTrait<T=Self> where T: MyTrait {
}

struct Impl1;
impl MyTrait for Impl1 {
}

struct Impl2;
impl MyTrait for Impl2 {
}

I now want a vector that contains elements of both implementations. As I learned here, I do:

fn foo() {
    let mut traits: Vec<Box<MyTrait>> = Vec::new();
    traits.push(Box::new(Impl1{}));
    traits.push(Box::new(Impl2{}));
}

But the compiler doesn't agree:

error[E0393]: the type parameter `T` must be explicitly specified
  --> src/main.rs:25
   |
25 |     let mut traits: Vec<Box<MyTrait>> = Vec::new();
   |                             ^^^^^^^ missing reference to `T`
   |
   = note: because of the default `Self` reference, type parameters must be specified on object types

On the one hand, that makes sense. On the other hand, what should I put for T? I want this to be generic, so I can't simply put there Impl1 or Impl2. I could do Vec<Box<MyTrait<MyTrait>>>, but this will only move the error, not resolve it.


Edit

The code above is the minimal failing one, here's a slightly less minimal implementation:

enum MyEnum<T: MyTrait> {
    A(T),
    B
}

pub trait MyTrait<T=Self> where T: MyTrait {
    fn do_stuff(self) -> MyEnum<T>;
}

struct Impl1;
impl MyTrait for Impl1 {
    fn do_stuff(self) -> MyEnum<Impl1> {
        MyEnum::A(self)
    }
}

struct Impl2;
impl MyTrait for Impl2 {
    fn do_stuff(self) -> MyEnum<Impl2> {
        MyEnum::B
    }
}

Each MyTrait object consumes itself and can either result in a MyEnum::A containing another MyTrait object or a MyEnum::B.

piegames
  • 975
  • 12
  • 31
  • 1
    What is the purpose of the generic parameter in `MyTrait`? Where are you using T in the trait? – user31601 Aug 02 '19 at 10:45
  • The edit suggests you have a problem in mind, but you are not explaining that, just showing specific, failing, attempt to solve it. Try to describe the higher level problem so we can try to guide you to whatever is preferred solution for that. `dyn Trait` and `enum`s have their own advantages and disadvantages and which you should use depends on the wider requirements you have. – Jan Hudec Aug 02 '19 at 11:38
  • Ad edit: but implementation *of what?* Mixing static (generics) and dynamic (trait objects, enums) polymorphism does not work well. You need to come up with another representation, and nobody can help you if they don't know representation of what it is supposed to be. – Jan Hudec Aug 02 '19 at 19:18

1 Answers1

2

Generic things—does not matter whether traits, types or functions—are not code that can be addressed, but merely templates for code to be generated when you substitute them. So they are not “object-safe”, i.e. you can't use them for types of dynamic references and smart pointers. You can only use their specific instances.

MyTrait is generic, so you can't have &dyn MyTrait or Box<dyn MyTrait>. You can only have &dyn MyTrait<Impl1> or Box<dyn MyTrait<Impl1>>.

You do have a default value for the parameter, but Self is special, because Self is the type that implements the trait, and as such only makes sense in the impl definition. But not in the free function where you are trying to declare Vec<Box<MyTrait>>. That's why it can't be compiled.

Also due to how special Self is, impl MyTrait for Impl1 gets desugared to impl MyTrait<Impl1> for Impl1 and impl MyTrait for Impl2 gets desugared to impl MyTrait<Impl2> for Impl2. Since MyTrait<Impl1> is a different trait from MyTrait<Impl2>, there is no common trait for Impl1 and Impl2 that you could use to box them and put them in a common vector.

You need a concrete, that is non-generic, trait for dynamic polymorphism.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172