Reputation: 451
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?
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>`
Upvotes: 0
Views: 652
Reputation: 58735
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:
let var: &dyn AssTrait<Item = i32>;
This now works because the Rust compiler knows the type of Item
. However, it forces that all implementations that you use also have Item = i32
.
You can make it a bit more flexible by using a trait object for the associated type too:
trait ItemBehavior {}
let var: &dyn AssTrait<Item = dyn ItemBehavior>;
// Or, depending on usage, perhaps:
let var: &dyn AssTrait<Item = &dyn ItemBehavior>;
// Or maybe:
let var: &dyn AssTrait<Item = Box<dyn ItemBehavior>>;
Now you can implement ItemBehaviour
for 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:
trait NonAssTrait {}
impl<T: AssTrait> NonAssTrait for T {}
fn main() {
let var: &dyn NonAssTrait;
}
Implementations of NonAssTrait
can delegate any method calls to the AssTrait
instance, provided that they don't expose the associated type.
Upvotes: 0