Home > OS >  How can I effectively "extend" the functionality of a function in sub-traits?
How can I effectively "extend" the functionality of a function in sub-traits?

Time:02-01

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.

CodePudding user response:

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.

  • Related