Why can a trait not construct itself?

2020-02-01 07:25发布

This code give me a compilation error:

trait IBoo {
    fn new() -> Box<IBoo>;
}

while this code compiles without any error:

trait IBoo {
    //fn new() -> Box<IBoo>;
}

trait IFoo {
    fn new() -> Box<IBoo>;
}
  1. Why does the first one not compile? rustc --explain E0038 does not give me a direct hint why it is not possible.
  2. Is it possible to combine construction and methods in one interface (trait)?

标签: rust
2条回答
Rolldiameter
2楼-- · 2020-02-01 07:45

The compiler tells you the exact reason this doesn't work:

error[E0038]: the trait `IBoo` cannot be made into an object
 --> src/main.rs:2:5
  |
2 |     fn new() -> Box<IBoo>;
  |     ^^^^^^^^^^^^^^^^^^^^^^ the trait `IBoo` cannot be made into an object
  |
  = note: method `new` has no receiver

Note the last line. It's telling you that the reason for the error is that new() doesn't depend on having an instance of a value implementing the IBoo trait.

By not taking a pointer of some kind to self, the method cannot be invoked by dynamic dispatch. If it can't be invoked by dynamic dispatch, this means it cannot go into the trait's associated vtable. There has to be an associated vtable, because that's how something like Box<IBoo> works. Some time ago, the core Rust developers decided that including even a single "non-object safe" method in a trait disqualified the whole trait from being used as an object.

To put it in other words: because you've defined a method that cannot be dynamically dispatched, the IBoo trait as a whole is disqualified from being used with dynamic dispatch.

If you want some kind of constructor function, you need to have some other way of writing that. This could be using plain function pointers, or an IBooFactory trait, as you might with Java.

查看更多
三岁会撩人
3楼-- · 2020-02-01 08:04

This is from the description of E0038:

Method has no receiver

Methods that do not take a self parameter can't be called since there won't be a way to get a pointer to the method table for them.

trait Foo {
    fn foo() -> u8;
}

This could be called as <Foo as Foo>::foo(), which would not be able to pick an implementation.

Adding a Self: Sized bound to these methods will generally make this compile.

trait Foo {
    fn foo() -> u8 where Self: Sized;
}

You can do this:

trait IBoo {
    fn new() -> Box<IBoo>
    where
        Self: Sized;
}

In other cases, you can place the restriction on the entire impl:

查看更多
登录 后发表回答