Help with Trait Bounds: Trait inheritance

How do I tell the rust compiler that any instance of the trait SpeaksWell must also satisfy the trait Speaks?

trait Speak {
     fn speak(&self);
}

trait SpeakWell: Speak {
    fn speak_well(&self);
}

struct P {}
impl Speak for P {
    fn speak(&self) {
        println!("Sup! Yo?")
    }
}

impl<T: Speak> SpeakWell for T where T: Speak {
    fn speak_well(&self) {
        println!("Hello there! General Kenobi?")
    }
}

pub fn dinner(speaker: &dyn SpeakWell){
    speaker.speak_well();
}

pub fn conference<T: Speak>(speaker: &dyn Speak){
    speaker.speak();
}

pub fn main(){
    let mut v: Vec<Box<dyn SpeakWell>> = Vec::new();
    v.push(Box::new(P{}));
    dinner(v[0].as_ref());
    conference(v[0].as_ref())
}

(Playground)

Rust doesn't have inheritance. The SpeakWell: Speak syntax means "if you implement SpeakWell, you also have to implement Speak", but the traits remain separate and unrelated. There is no automatic subclassing in dyn.

You will have to add as_speak(&self) -> &dyn Speak to SpeakWell and implement the cast.

I suggest "flattening" hierarchies for Rust. Traditional OOP architecture won't fit it well.

See this blog post for a nice way to do this,

https://llogiq.github.io/2020/03/14/ootb.html

1 Like

Here is an example using @kornel's suggestion:

pub
trait Speak : AsDynSpeak {
    fn speak (self: &'_ Self)
    ;
}
    // AsDynSpeak let's us add to the vtables of all "children"
    // a virtual method to (up)cast to a `dyn Speak` ("parent")
    pub
    trait AsDynSpeak {
        fn as_dyn_speak (self: &'_ Self) -> &'_ dyn Speak
        ;
    }
    impl<T : Speak> AsDynSpeak for T {
        #[inline]
        fn as_dyn_speak (self: &'_ Self) -> &'_ dyn Speak
        {
            self
        }
    }

pub
trait SpeakWell : Speak {
    fn speak_well (self: &'_ Self)
    ;
}

////// implementors

struct P {}
impl SpeakWell for P {
    fn speak_well (self: &'_ Self)
    {
        println!("The senate will decide your fate.")
    }
}
impl Speak for P {
    fn speak (self: &'_ Self)
    {
        println!("*I* am the senate!")
    }
}

// ... use `.as_dyn_speak()` to upcast a `dyn SpeakWell` to a `dyn Speak`

Another option is to use &'_ (impl Speak + ?Sized) (i.e., accept anything that implements Speak, including all the trait objects (?Sized) that so do), instead of &'_ (dyn Speak) (i.e., accept exactly the "parent" trait object dyn Speak):

pub
trait Speak {
    fn speak (self: &'_ Self)
    ;
}

pub
trait SpeakWell : Speak {
    fn speak_well (self: &'_ Self)
    ;
}

////// implementors

struct P {}
impl SpeakWell for P {
    fn speak_well (self: &'_ Self)
    {
        println!("The senate will decide your fate.")
    }
}
impl Speak for P {
    fn speak (self: &'_ Self)
    {
        println!("*I* am the senate!")
    }
}

pub
fn dinner (speaker: &'_ (impl SpeakWell + ?Sized))
{
    speaker.speak_well();
}

pub
fn conference (speaker: &'_ (impl Speak + ?Sized))
{
    speaker.speak();
}
  • Playground

  • impl ... in argument position is sugar for a(n anonymous) generic type parameter:

    fn conference<T : Speak + ?Sized> (speaker: &'_ T)
    

Thanks. I've also heeded @kornel's advice and refactored my code quite a bit.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.