The answer is very simple - it's impossible to call the self-consuming method on trait object.
The key words are object safety. Essentially, this property combines two requirements:
- every method must work with
self through some kind of indirection;
- every method must be fully identifiable via original type (i.e. no generics).
To see this in more detail, let's actually try to code something and ask compiler's opinion. First of all, just trying to define the trait:
trait Consumer {
fn consume(self);
}
Compiler is already unhappy:
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> src/lib.rs:2:16
|
2 | fn consume(self);
| ^^^^ doesn't have a size known at compile-time
|
help: consider further restricting `Self`
|
2 | fn consume(self) where Self: Sized;
| ^^^^^^^^^^^^^^^^^
help: function arguments must have a statically known size, borrowed types always have a known size
|
2 | fn consume(&self);
| ^
OK, we can be even more conservative then the compiler's advice and add the restriction on trait. Then, add a stub for trait object creation:
trait Consumer where Self: Sized {
fn consume(self);
}
fn main() {
let _: Box<dyn Consumer> = todo!();
}
Now, the error is slightly more complex:
error[E0038]: the trait `Consumer` cannot be made into an object
--> src/main.rs:6:12
|
6 | let _: Box<dyn Consumer> = todo!();
| ^^^^^^^^^^^^^^^^^ `Consumer` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> src/main.rs:1:28
|
1 | trait Consumer where Self: Sized {
| -------- ^^^^^ ...because it requires `Self: Sized`
| |
| this trait cannot be made into an object...
There is one workaround, however: it's not necessary to restrict the whole trait - just the offending method, as we were told from the start. Moving where clause, as here:
trait Consumer {
fn consume(self) where Self: Sized;
}
...makes the code above compile.
Now, what about actually using this trait object? Let's implement it, for example, for unit type, and use this from main:
trait Consumer {
fn consume(self) where Self: Sized;
}
impl Consumer for () {
fn consume(self) {}
}
fn main() {
let consumer: Box<dyn Consumer> = Box::new(());
consumer.consume();
}
Another compiler error!
error: the `consume` method cannot be invoked on a trait object
--> src/main.rs:11:14
|
2 | fn consume(self) where Self: Sized;
| ----- this has a `Sized` requirement
...
11 | consumer.consume();
| ^^^^^^^
Again, the restriction we've placed on method forbids the code which would make no sense if it compiled.