Issue with trait objects (super trait / extension trait)

Hi there,
given the follwoing code:

trait Foo {}

trait Bar: Foo {}

struct MyBar {}

impl Foo for MyBar {}
impl Bar for MyBar {}

fn store_foo_like(foo_like: Box<dyn Foo>) {}

fn get_bar_like() -> Box<dyn Bar> {
    Box::new(MyBar {})
}


fn main() {
    let mybar = Box::new(MyBar {});
    store_foo_like(mybar);
    
    store_foo_like(
        get_bar_like()
    );
}

The first call to store_foo_like is compiling fine, but the second one is not with the error:

             get_bar_like()
   |         ^^^^^^^^^^^^^^ expected trait `Foo`, found trait `Bar`
   |
   = note: expected struct `std::boxed::Box<(dyn Foo + 'static)>`
              found struct `std::boxed::Box<(dyn Bar + 'static)>`

Why is this the case ? Why does the dyn Bar does not "include" the dyn Foo as defined by the trait and make those types incompatible (which is kinda working in the first call where I pass a Box with the structure inside that implements Bar as well as Foo)?

Well another solution could be to use generics with trait bounds, however this is not applicable in my use case as I'd like to put the function I'm calling within a dynamic library where no generics would be allowed to be exported as their size is unknown at compile time (as far as I'm aware) - so I do have to use trait objects here.

So any hint would be much appreciated :wink:

Rust does not offer (yet) automatic upcasting of trait objects, so you need to explicitly implement those that you wish to have and use:

trait Bar : Foo
{
    fn upcast<'self_> (self: Box<Self>)
      -> Box<dyn Foo + 'self_>
    where
        Self : 'self_,
    ;
}

default
impl<T : Foo> Bar for T {
    fn upcast<'self_> (self: Box<Self>)
      -> Box<dyn Foo + 'self_>
    where
        Self : 'self_,
    {
        self
    }
}
// ...

    store_foo_like(
        get_bar_like().upcast()
    );

Now, the above code is relying on an unstable (and thus nightly-only) feature: specialization. I've done that because the resulting code is very readable / clealry describes what is going on.

Now, here is the version that works on stable Rust, but at the cost of "messier" code for those who haven't stumbled upon it yet:

trait Bar : Foo
    + FooUpcast // or make it a super trait of `Foo` itself
{}

trait FooUpcast {
    fn upcast<'self_> (self: Box<Self>)
      -> Box<dyn Foo + 'self_>
    where
        Self : 'self_,
    ;
}

impl<T : Foo> FooUpcast for T {
    fn upcast<'self_> (self: Box<Self>)
      -> Box<dyn Foo + 'self_>
    where
        Self : 'self_,
    {
        self
    }
}
// ...

    store_foo_like(
        get_bar_like().upcast()
    );
1 Like

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