Here is an offending example:
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
// Struct that holds a collection of these traits.
struct Example<'a> {
behaviours: Vec<&'a Behaviour>,
}
impl<'a> Example<'a> {
fn add_behaviour<T: Behaviour>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
I am getting:
error[E0191]: the value of the associated type `Sub` (from the trait `Behaviour`) must be specified
--> src/main.rs:17:25
|
17 | behaviours: Vec<&'a Behaviour>,
| ^^^^^^^^^ missing associated type `Sub` value
Why must this type must be specified, particularly in this case where we are only storing a reference to the object? How can I get this code to work?
So the answer to your first question is covered by Tim's answer and is correct, you might want not want your
Example
to be generic. In that case, you need to use some sort of type erasure:Within the closure, you have access to all the types needed call the traits functions with whatever subtype needed.
Why this happens, is mostly because you actually need a definition of the associated type in order for the trait to be "complete" so the compiler can work with it. Tims answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.
You need to specify the associated type of the trait (i.e.
Behavior<Sub = ???>
).When adding the associated type at all places, it compiles:
See this in action on the Playground
All types must be statically known at compile time. If Rust would allow different associated types for elements of a
Vec
, type information could depend on indices which are only known at runtime.I find it helpful to consider a smaller example:
You were on the right track to fixing this though. I assume you introduced the
SubBehaviour
trait because you realized you need to put restrictions of whatT
can be. The thing is, in that case you don't need an associated type anymore.The only limitation is that in your definition of
Behaviour
you can not do anything which would depend on the size ofT
, (like allocating it on the stack or moving it) since the size ofT
can not be specified by theSubBehaviour
trait.