Henning Koehler
Henning Koehler

Reputation: 2657

Rust: returning trait object in default method implementation

When trying to return a trait object from a default implementation within a trait, such as

trait Foo {
    fn foo(self: Box<Self>) -> Box<dyn Foo> {
        self
    }
}

the Rust compiler complains that

error[E0277]: the size for values of type `Self` cannot be known at compilation time
note: required for the cast to the object type `dyn Foo`

The type cast should be from Box<Self> to Box<dyn Foo>, why would this require Self to be sized?

Upvotes: 2

Views: 79

Answers (1)

Cerberus
Cerberus

Reputation: 10317

It's impossible to convert an unsized type to trait object, since this will force it to have two sets of metadata at once.

Consider this code:

trait Foo: 'static {
    fn foo(self: Box<Self>) -> Box<dyn Foo> {
        self
    }
}

trait Bar {}

impl Foo for dyn Bar {}

impl Foo for i32 {}

impl<T> Bar for T {}

fn main() {
    let boxed = Box::new(42);
    boxed.foo();

    let boxed: Box<dyn Bar> = Box::new(42);
    boxed.foo();
}

In this case, there's nothing which would forbid the last call - all the signatures allow it. But, if we coerce Box<dyn Bar> to Box<dyn Foo>, where would the virtual functions table for dyn Bar go? (It's empty in this case, of course, but it still have to exist - empty traits are not special)

If you add where Self: Sized to foo, however, the last call is properly rejected:

error: the `foo` method cannot be invoked on a trait object
  --> src/main.rs:20:11
   |
2  |     fn foo(self: Box<Self>) -> Box<dyn Foo> where Self: Sized {
   |                                                         ----- this has a `Sized` requirement
...
20 |     boxed.foo();
   |           ^^^

Upvotes: 5

Related Questions