Say I need to model some living things. All living things need to do stuff:
trait LivingThing {
fn do_stuff(&self);
}
I might have sub-traits like animals and plants that do different things. Say I model an animal like this:
trait Animal {
fn do_other_stuff(&self);
fn food(&self) {
println!("Looking for food!");
}
}
impl LivingThing for dyn Animal {
fn do_stuff(&self) {
self.food();
self.do_other_stuff();
}
}
The food
function does not need self
at the moment, but I am just trying to keep things simple here. The do_other_stuff
function exists to ensure sub-traits can add their own functionality which is automatically called when calling the do_stuff
function. Like a human, for example:
struct Human {}
impl Animal for Human {
fn do_other_stuff(&self) {
println!("Taking a shower!");
}
}
I can now do this to automatically have the Human
functionality as well as the Animal
functionality.
let some_human = Human {};
LivingThing::do_stuff(&some_human as &dyn Animal);
// Prints: Looking for food!
// Taking a shower!
What I dislike about this is that I can't do this
let some_human = Human {};
some_human.do_stuff();
because Human
doesn't implement the LivingThing
trait. But I feel like there is no conflict because Human implements Animal
which implements LivingThing
. Sure, Human
could implement 10 different traits that all implement LivingThing
but if there is just one implementation it should be clear what I am trying to do. Do I just have to live with this? Or is there a better way to add to the functionality of do_stuff
in sub-traits?
What I have done until now is invert the model: Have Human
implement LivingThing
and Animal
and have a trait-bound for Animal: LivingThing
. The issue is, that I need to remember to call the Animal
's food
function from the Human
which is not ideal.
Your other option would be to use generics to implement LivingThing
for all types that implement Animal
impl<T> LivingThing for T
where T: Animal {
fn do_stuff(&self) {
self.food();
self.do_other_stuff();
}
}
However, until specialization is stabilized, this approach means directly implementing LivingThing
on any type that implements Animal
(such as Human
) will cause an implementation conflict.