Methods implemented on Base trait not carry over to Derived trait object


#1

(Issue discovered in another thread.)

Why the error below? Type implements Base and Derived. Moreover, additional methods are defined for Base and Derived. A Derived trait object is not able to use the additional methods defined for Base.

trait Base { fn base(&self) -> u32; }
trait Derived: Base {}

struct Type;
impl Base for Type { fn base(&self) -> u32 { 1 } }
impl Derived for Type {}


// Additional methods for everything implementing trait Base.
impl Base {
  fn double_base(&self) -> u32 { self.base() * 2 }
}

// Additional methods for everything implementing trait Derived.
impl Derived {
  fn triple(&self) -> u32 { self.base() * 3 }
}


fn main() {
    let b: Box<Derived> = Box::new(Type);
    println!("b.base() = {:?}", b.base());

    // error: no method named `double_base` found for type `Derived` in the
    //        current scope
    //println!("b.double_base() = {:?}", (*b).double_base());

    println!("b.triple() = {:?}", b.triple());
}

Playground

The additional methods implemented on Base (double_base()), can only depend on the methods constituting Base (base()) which are accessible in the Derived trait object’s vtable. So why not allow them? Is there a fundamental reason, or is this a possible oversight/un-implemented feature?


Upcasting support for arbitrary types implementing a trait: Any vs MOPA
#2

And on a closely-related note

    let c = &*b as &Base;

in main() gives

 error: non-scalar cast: `&Derived` as `&Base`

but as far as I can see, the Derived trait object reference should be convertible to a Base one – the latter is just a subset of the vtable coupled with the same data object.


#3

See http://stackoverflow.com/questions/28632968/why-doesnt-rust-support-trait-object-upcasting .


#4

If you’re looking for an actual solution, you can use extension traits.

// Additional methods for everything implementing trait Base.
trait BaseExt: Base {
    fn double_base(&self) -> u32 { self.base() * 2 }
}
impl<T: ?Sized + Base> BaseExt for T { }

// Additional methods for everything implementing trait Derived.
trait DerivedExt: Derived {
    fn triple(&self) -> u32 { self.base() * 3 }
}
impl<T: ?Sized + Derived> DerivedExt for T { }

#5

Ah, I got so excited figuring out the real cause in the other thread that I didn’t actually google it. Shame on me. :wink: Also noteworthy is that #5665 – this feature is intended.

Thanks! That seems like a generally better way than just implementing methods a trait.