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>;
}
- Why does the first one not compile?
rustc --explain E0038
does not give me a direct hint why it is not possible.
- Is it possible to combine construction and methods in one interface (trait)?
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.
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: