trait FnBox {
fn call_box(self: Box<Self>);
}
impl<F: FnOnce()> FnBox for F {
fn call_box(self: Box<F>) {
(*self)()
}
}
fn main() {
let job: Box<FnOnce()> = Box::new(|| {});
// versions that compile
// let job = Box::new(|| {});
// let job: Box<FnBox> = Box::new(|| {});
job.call_box();
}
job.call_box();
doesn't compile:
error[E0599]: no method named `call_box` found for type `std::boxed::Box<std::ops::FnOnce()>` in the current scope --> src/main.rs:16:9 | 16 | job.call_box(); | ^^^^^^^^ | = note: job is a function, perhaps you wish to call it = note: the method `call_box` exists but the following trait bounds were not satisfied: `std::ops::FnOnce() : FnBox` = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `call_box`, perhaps you need to implement it: candidate #1: `FnBox`
Why doesn't the compiler see the conversion to FnBox
here?
I suppose when I use let job = Box::new(|| {});
that results in a Box<closure>
type. The compiler manages to resolve job.call_box()
, which is Box<closure>
-> Box<FnOnce()>
-> Box<FnBox>
(because FnBox
is implemented for FnOnce()
, not for closure
). Why doesn't just Box<FnOnce()>
-> Box<FnBox>
work?
FnBox
isn't implemented forFnOnce()
.FnOnce()
has two meanings: as a trait, and as a type. InBox<FnOnce()>
, it's a (dynamic, unsized) type. Since Rust 1.27, the type can be written more explicitly asdyn FnOnce()
. For the rest of this answer I will usedyn
in order to make clear when I'm talking about the trait and when about the dynamic type.The
impl
you wrote doesn't implementFnBox
for the typedyn FnOnce()
, because generic parameters have an implicitSized
bound. If we follow the advice of that question's answer and add+ ?Sized
toF
, the compiler does give a slightly more helpful error message:Which does explicitly call out the place where
Sized
ness is required.In versions of Rust before 1.35, there was no way to fix this error; trait objects of
FnOnce()
were just useless. However, todayBox<dyn FnOnce()>
implementsFnOnce()
, so you can call it like a normal closure, without using*
:See also