I want to create a variable that holds a trait. The trait implementation is unknown during compile time. Hence, I need a trait object. This works with "normal" traits but not when the trait has an associated type that is unknown during compile time.
Why? Let AssTrait be a trait associating a type and AssTraitImpl a struct implementing that trait (see example below). Now a trait object for an instance of AssTraitImpl could just point to the vtable representing the methods implemented for AssTraitImpl. Or am I wrong?
Example
The code below is not working. However it is, if we remove the associated type from the trait.
trait AssTrait {
type Item;
}
struct AssTraitImpl {
}
impl AssTrait for AssTraitImpl {
type Item = i32;
}
fn main() {
let var: &dyn AssTrait;
}
I get this error message:
error[E0191]: the value of the associated type `Item` (from trait `AssTrait`) must be specified
--> src/main.rs:20:20
|
9 | type Item;
| --------- `Item` defined here
...
20 | let var : &dyn AssTrait;
| ^^^^^^^^ help: specify the associated type: `AssTrait<Item = Type>`
Since the type is unknown until runtime, so is the associated type. Technically, in your example, Rust doesn't need to care about that because you aren't actually using it. But this is quite uncommon example code. In a more realistic example, it is unusual for an associated type not to be used in any of the trait's method arguments or return types. Rust chooses to disallow unspecified associated types rather than to check every method to see if it's unused. Probably this could be changed in the future, as it would be backwards-compatible, but I can't see much value in it apart from some very rare use-cases.
As I hinted, if the associated type is specified then it is allowed:
This now works because the Rust compiler knows the type of
Item. However, it forces that all implementations that you use also haveItem = i32.You can make it a bit more flexible by using a trait object for the associated type too:
Now you can implement
ItemBehaviourfor each possible associated type, giving it whatever methods or constraints you need when using the type.Another possible workaround is to create another trait that doesn't have an associated type and implement it for all types that implement the other trait:
Implementations of
NonAssTraitcan delegate any method calls to theAssTraitinstance, provided that they don't expose the associated type.