Why isn't this supertrait working?

I have two traits, defined as such:

trait Walking {
    fn walk(&self);
}

trait HopOnOneLeg: Walking {
    fn hop_on_one_leg(&self);
}

Because of course everything that wants to hop_on_one_leg() must be able to walk().

So I write:


impl AthleticRun
    fn take_a_walk(&self, walker: &dyn Walking) {
        // no error
        walker.walk();
    }

    fn warm_up_for_a_run(&self, sportman: &dyn HopOnOneLeg) {

        self.take_a_walk(sportman);
        //               ^^^^^^^^ expected trait `Walking`, found trait `HopOnOneLeg`

        // no error
        sportman.hop_on_one_leg();
    }
}

What am I missing here ?

You can't upcast trait objects like that in Rust unfortunately. If you implement Walking for &T where T: Walking + ?Sized you can pass in an &&dyn HopOnOneLeg which can be coerced to &dyn Walking.

Thanks, but damn, what part of this explanation of Rust by Example did I not understand then ? I have roughly the same syntax.

That example in the book does not include any upcasting - never does it attempt to convert &dyn CompSciStudent to &dyn Programmer or &dyn Student. Although an &dyn CompSciStudent will allow you to call methods defined in the Student or Programmer traits, the type itself cannot be changed to use a different trait.

1 Like

Okay thanks for your time and explanation.

You can make your example compile by making the take_a_walk function compatible with all Walking types, rather than just dyn Walking. (The ?Sized bound is necessary to make it work with trait object types like dyn HopOnOneLeg.)

impl AthleticRun {
    fn take_a_walk<W: Walking + ?Sized>(&self, walker: &W) {
        walker.walk();
    }

    fn warm_up_for_a_run(&self, sportman: &dyn HopOnOneLeg) {
        self.take_a_walk(sportman);
        sportman.hop_on_one_leg();
    }
}
5 Likes