You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
However, Box<dyn FnOnce()> drops out of the modified impl.
To rescue this, we had had a temporary solution called fnbox.
Unfortunately, due to minor coherence reasons, fnbox and
FnOnce for Box<impl FnMut> had not been able to coexist.
We had preferred fnbox for the time being.
Now, as unsized_locals is implemented, we can just write the
original impl:
impl<A,F:FnOnce<A> + ?Sized>FnOnceforBox<F>{typeOutput = <FasFnOnce<A>>::Output;extern"rust-call"fncall_once(self,args:A) -> Self::Output{// *self is an unsized rvalue
<FasFnOnce<A>>::call_once(*self, args)}}
However, since unsized_locals is a very young feature, we're careful about
this FnOnce impl now.
There's another reason for instability: for compatibility with fnbox,
we currently allow specialization of the Box<impl FnOnce> impl:
impl<A,F:FnOnce<A> + ?Sized>FnOnceforBox<F>{typeOutput = <FasFnOnce<A>>::Output;// we have "default" heredefaultextern"rust-call"fncall_once(self,args:A) -> Self::Output{
<FasFnOnce<A>>::call_once(*self, args)}}
As an analogy to &dyn Fn() and &mut dyn FnMut(), you may have expected
Box<dyn FnOnce()> to work. But it hadn't until the recent improvement!
FnBox had been a temporary solution for this until we are able to pass
trait objects by value.
Spoiler: boxed_closure_impls actually implements
Box<dyn FnOnce()>! This didn't work because we lacked features like
unsized_locals for a long time. Therefore, this section
just explains historical reasons for FnBox.
First approach: just provide Box adapter impl
The first (and natural) attempt for Box<dyn FnOnce()> would look like:
However, this doesn't work. We have to relax the Sized bound for F because
we expect trait objects here, but *self must be Sized because it is passed
as a function argument.
The second attempt: add FnOnce::call_box
One may come up with this workaround: modify FnOnce's definition like this:
pubtraitFnOnce<Args>{typeOutput;extern"rust-call"fncall_once(self,args:Args) -> Self::Output;// Add this new methodextern"rust-call"fncall_box(self:Box<Self>,args:Args) -> Self::Output;}
...and then, modify the impl like this:
impl<A,F:FnOnce<A> + ?Sized>FnOnce<A>forBox<F>{typeOutput = <FasFnOnce<A>>::Output;extern"rust-call"fncall_once(self,args:A) -> Self::Output{// We can use `call_box` here!
<FasFnOnce<A>>::call_box(self, args)}// We'll have to define this in every impl of `FnOnce`.extern"rust-call"fncall_box(self:Box<Self>,args:A) -> Self::Output{
<FasFnOnce<A>>::call_box(*self, args)}}
What's wrong with this? The problem here is crates:
FnOnce is in libcore, as it shouldn't depend on allocations.
Box is in liballoc, as it:s the very allocated pointer.
It is impossible to add FnOnce::call_box because it is reverse-dependency.
There's another problem: call_box can't have defaults.
default impl from the specialization RFC may resolve this problem.
The third attempt: add FnBox that contains call_box
call_box can't reside in FnOnce, but how about defining a new trait in
liballoc?
FnBox is almost a copy of FnOnce, but with call_box:
For Sized types (from which we coerce into dyn FnBox), we define
the blanket impl that proxies calls to FnOnce:
impl<A,F:FnOnce<A>>FnBox<A>forF{typeOutput = <FasFnOnce<A>>::Output;extern"rust-call"fncall_box(self:Box<Self>,args:A) -> Self::Output{// Here we assume `F` to be sized.
<FasFnOnce<A>>::call_once(*self, args)}}
Now it looks like that we can define FnOnce for Box<F>.
Firstly, the actual implementation is different from the one presented above.
Instead of implementing FnOnce for Box<impl FnBox>, liballoc only
implements FnOnce for Box<dyn FnBox>.
Note that dyn FnBox(&i32) desugars to
dyn for<'r> FnBox<(&'r i32,), Output = ()>.
It isn't covered in dyn FnBox<A, Output = R> + 'a or
dyn FnBox<A, Output = R> + Send + 'a due to HRTB.
However, we hadn't been able to write these in presense of FnBox
(until boxed_closure_impls lands).
To have FnMut<A> for Box<F>, we should have (at least) this impl:
// Note here we only impose `F: FnMut<A>`.// If we can write `F: FnOnce<A>` here, that will resolve all problems.impl<A,F:FnMut<A> + ?Sized>FnOnce<A>forBox<F>{// ...}
Unfortunately, the compiler complains that it overlaps with our
dyn FnBox() impls. At first glance, the overlap must not happen.
The A generic parameter does the trick here: due to coherence rules,
a downstream crate may define the following impl:
The trait solver doesn't know that A is always a tuple type, so this is
still possible. With this in mind, the compiler emits the overlap error.
Modification
For compatibility with boxed_closure_impls,
we now have a slightly modified version of FnBox:
// It's now a subtrait of `FnOnce`pubtraitFnBox<Args>:FnOnce<Args>{// now uses FnOnce::Output// type Output;extern"rust-call"fncall_box(self:Box<Self>,args:Args) -> Self::Output;}
The future of fnbox
FnBox has long been considered a temporary solution for Box<FnOnce>
problem. Since we have boxed_closure_impls now,
it may be deprecated and removed in the future.