I am trying to understand how polymorphism works when using a trait with an associated type. Consider the following trait:
trait Animal {
fn talk (&self);
}
This trait is used by the following structures:
struct Dog;
struct Cow;
impl Animal for Dog {
fn talk (&self) {
println!("Woof");
}
}
impl Animal for Cow {
fn talk (&self) {
println!("Moo");
}
}
Then I loop over a Vec<&Animal>, and polymorphism works well here:
fn main() {
let animals: Vec<&Animal> = vec![&Dog, &Cow];
for animal in &animals {
animal.talk();
}
}
// output:
// Woof
// Moo
So far so good. Now, I add an associated type Food to the trait (the type is not used, but it is only for the minimal repro).
struct Meat;
struct Herb;
trait Animal {
type Food;
...
}
impl Animal for Dog {
type Food = Meat;
...
}
impl Animal for Cow {
type Food = Herb;
...
}
And now I get the error:
error[E0191]: the value of the associated type `Food` (from trait `Animal`) must be specified
--> src/main.rs:188:23
163 | type Food;
| ---------- `Food` defined here
...
188 | let animals: Vec<&Animal> = vec![&Dog, &Cow];
| ^^^^^^ help: specify the associated type: `Animal<Food = Type>`
But in this case, I cannot just follow the error message since the number of structs implementing the trait Animal is not supposed to be static.
What is the Rust way to solve that ? Thanks in advance
&Animal is short for &dyn Animal. dyn Animal is a trait object type, and it only exists for a given trait if the trait is object-safe. Traits with associated types are not object-safe, because dyn Animal cannot implement Animal without specifying the associated type Food.¹
This is an inherent limitation of runtime polymorphism (trait objects): you don't know the concrete type, so you can't know its associated type.²
If you want to create a vector of things you can call .talk() on, it's easy enough to create a trait just for that (playground):
trait Talk {
fn talk(&self);
}
impl<A: Animal> Talk for A {
fn talk(&self) {
Animal::talk(self);
}
}
let animals: Vec<&dyn Talk> = vec![&Dog, &Cow];
You won't be able to write any code that uses Food via a &dyn Talk, which is the point: Food is dependent on the concrete type, and your vector contains multiple concrete types.
¹ You can make a trait object of types that all have the same associated types, like dyn Animal<Food = Herb>. This is commonly seen with Iterator, as in Box<dyn Iterator<Item = i32>>. But it does not solve the problem when the Animals have different kinds of Food.
² With compile-time polymorphism (generics), you can write code that is generic over anything that implements Animal with any Food type. But you can't put things of different compile-time types in a Vec, so that doesn't help either.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With