The following code compiles without warning on nightly 1.7.0:
trait FnBox {
fn call_box(self: Box<Self>);
}
impl <F: FnOnce()> FnBox for F {
fn call_box(self: Box<F>) {
(*self)()
}
}
fn main() {}
But when I make this slight modification, which I thought meant the exact same thing, I get an error about FnOnce
being unsized and not movable.
trait FnBox {
fn call_box(self: Box<Self>);
}
impl FnBox for FnOnce() {
fn call_box(self: Box<FnOnce()>) {
(*self)();
}
}
fn main() {}
Error message:
error[E0161]: cannot move a value of type dyn std::ops::FnOnce(): the size of dyn std::ops::FnOnce() cannot be statically determined
--> src/main.rs:7:9
|
7 | (*self)();
| ^^^^^^^
What is the difference between these two examples, and why are there no problems with the first one?
There is a great difference, actually. In the first piece of code:
impl<F: FnOnce()> FnBox for F {
fn call_box(self: Box<F>) {
(*self)()
}
}
it is declared that for any type F
which implements FnOnce
we implement FnBox
. F
is a concrete type, and at each call site call_box()
method will be monomorphized. The concrete type of F
at each call site, as well as its size, is known to the compiler, so there are no problems with this definition.
In the second piece of code, however:
impl FnBox for FnOnce() {
fn call_box(self: Box<FnOnce()>) {
(*self)();
}
}
it is declared that bare trait object type implements FnBox
. However, this implementation is unsound: while Box<FnOnce()>
is a correct, sized type which is suitable for variables and function arguments, FnOnce()
by itself is not - it is a bare trait object type and it is unsized, that is, its size is not known to the compiler. This places several restrictions on what you can do with this type, and one of the main restrictions is that you cannot use values of this type by value. However, this is exactly what happens in this code: you attempt to dereference Box<FnOnce()>
to get FnOnce()
.
Previously by-value self
methods would meant that the trait is not object-safe, and because FnOnce::call_once
consumes the implementing instance by value, it wouldn't be object-safe. However, by-value self
methods do not make the trait object-unsafe since RFC 817 got implemented. By-value self
methods still can't be called on a trait object according to the above reasoning, though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With