Can a trait give default implementation for *some* methods of a parent trait?

Viewed 444

Suppose we have a basic trait and an advanced trait as follows:

pub trait BasicTrait {
    fn key_method(&self);


    fn other_method(&self);
}

pub trait AdvancedTrait: BasicTrait {
    fn key_method_with_argument(&self, parameter: u32);
}

Now, every time someone will implement AdvancedTrait, the most likely implementation of BasicTrait::key_method(&self) is calling key_method_with_argument with some default argument. How can I provide this default implementation (idiomatically) so that anyone implementing AdvancedTrait will (1) only need to implement key_method_with_argument and any other required methods from BasicTrait, and (2) optionally implement key_method() and override the default implementation, only if needed?

Related questions:

having an impl block as proposed in the answer here does not work since the code is expected to implement all other methods of BasicTrait.

2 Answers

You can do it by explicitly duplicating the BasicTrait methods into AdvancedTrait, ask your users to only implement AdvancedTrait, and then do a blanket impl for anything implementing AdvancedTrait to also implement BasicTrait. It is probably advisable to give the duplicated methods a name indicating they are just for implementing BasicTrait, and to prevent ambiguous calls:

pub trait BasicTrait {
    fn key_method(&self);
    fn other_method(&self);
}

pub trait AdvancedTrait: BasicTrait {
    fn key_method_impl(&self) {
        self.key_method_with_argument(0)
    }
    fn other_method_impl(&self);


    fn key_method_with_argument(&self, parameter: u32);
}

impl<T: AdvancedTrait> BasicTrait for T {
    fn key_method(&self) {
        AdvancedTrait::key_method_impl(self)
    }
    
    fn other_method(&self) {
        AdvancedTrait::other_method_impl(self)
    }
}

You could also call key_method_impl basic_key_method instead, a bunch of naming options exist.

The feature you want is called specialization, and unfortunately it is nightly-only and has known soundness holes, so don't expect it to stabilize soon. However, here's how it looks like with it:

#![feature(specialization)]

pub trait BasicTrait {
    fn key_method(&self);

    fn other_method(&self);
}

pub trait AdvancedTrait: BasicTrait {
    fn key_method_with_argument(&self, parameter: u32);
}

default impl<T: AdvancedTrait> BasicTrait for T {
    default fn key_method(&self) {
        self.key_method_with_argument(12345);
    }
}

Playground.

Related