How does the trait upcast coercion workaround work (trait method for getting an instance of the supertrait)?

Viewed 52

It looks like Rust does not allow using a reference to an instance of a trait to be used where a reference to an instance of the supertrait was expected:

trait Animal{}
trait Dog: Animal{}

fn convert(dog: &Dog) -> &Animal {
    dog // Error: trait upcasting coercion is experimental, see issue #65991 ...
}

But there's a workaround, used often in the Rust-Analyzer codebase. How does the workaround work?

Here's the workaround, which enables us to write a convert that rustc will accept:

trait Animal{}
trait Dog: Animal{
    fn upcast(&self) -> &Animal; // added line
}
fn convert(dog: &Dog) -> &Animal {
    dog.upcast() // changed line
}

And here's an implementation of upcast:

struct Foo {}
impl Animal for Foo {}
impl Dog for Foo {
    fn upcast(&self) -> &Animal {
        self
    }
}

Comparing the upcast and the original convert: they have the same signature and same implementation (modulo renaming). So it looks like something is allowed for methods that is not allowed for functions. What happened?

1 Answers

&self in upcast() is not &dyn Dog, it is the concrete &Foo. This is usual call to a method of a dyn Trait.

Related