about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/unstable-book/src/library-features/fnbox.md250
1 files changed, 15 insertions, 235 deletions
diff --git a/src/doc/unstable-book/src/library-features/fnbox.md b/src/doc/unstable-book/src/library-features/fnbox.md
index 3200601e557..16ebab3abeb 100644
--- a/src/doc/unstable-book/src/library-features/fnbox.md
+++ b/src/doc/unstable-book/src/library-features/fnbox.md
@@ -6,247 +6,27 @@ The tracking issue for this feature is [#28796]
 
 ------------------------
 
-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.
-
-See [`boxed_closure_impls`][boxed_closure_impls] for the newer approach.
-
-[boxed_closure_impls]: library-features/boxed-closure-impls.html
-
-## Usage
-
-If you want to box `FnOnce` closures, you can use `Box<dyn FnBox()>` instead of `Box<dyn FnOnce()>`.
+This had been a temporary alternative to the following impls:
 
 ```rust
-#![feature(fnbox)]
-
-use std::boxed::FnBox;
-
-fn main() {
-    let resource = "hello".to_owned();
-    // Create a boxed once-callable closure
-    let f: Box<dyn FnBox() -> String> = Box::new(|| resource);
-
-    // Call it
-    let s = f();
-    println!("{}", s);
-}
+impl<A, F> FnOnce for Box<F> where F: FnOnce<A> + ?Sized {}
+impl<A, F> FnMut for Box<F> where F: FnMut<A> + ?Sized {}
+impl<A, F> Fn for Box<F> where F: Fn<A> + ?Sized {}
 ```
 
-## How `Box<dyn FnOnce()>` did not work
-
-**Spoiler**: [`boxed_closure_impls`][boxed_closure_impls] actually implements
-`Box<dyn FnOnce()>`! This didn't work because we lacked features like
-[`unsized_locals`][unsized_locals] for a long time. Therefore, this section
-just explains historical reasons for `FnBox`.
-
-[unsized_locals]: language-features/unsized-locals.html
-
-### First approach: just provide `Box` adapter impl
-
-The first (and natural) attempt for `Box<dyn FnOnce()>` would look like:
-
-```rust,ignore
-impl<A, F: FnOnce<A> + ?Sized> FnOnce<A> for Box<F> {
-    type Output = <F as FnOnce<A>>::Output;
-
-    extern "rust-call" fn call_once(self, args: A) -> Self::Output {
-        <F as FnOnce<A>>::call_once(*self, args)
-    }
-}
-```
-
-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:
-
-```rust,ignore
-pub trait FnOnce<Args> {
-    type Output;
-
-    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
-    // Add this new method
-    extern "rust-call" fn call_box(self: Box<Self>, args: Args) -> Self::Output;
-}
-```
-
-...and then, modify the `impl` like this:
-
-```rust,ignore
-impl<A, F: FnOnce<A> + ?Sized> FnOnce<A> for Box<F> {
-    type Output = <F as FnOnce<A>>::Output;
-
-    extern "rust-call" fn call_once(self, args: A) -> Self::Output {
-        // We can use `call_box` here!
-        <F as FnOnce<A>>::call_box(self, args)
-    }
-    // We'll have to define this in every impl of `FnOnce`.
-    extern "rust-call" fn call_box(self: Box<Self>, args: A) -> Self::Output {
-        <F as FnOnce<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`:
-
-```rust,ignore
-pub trait FnBox<Args> {
-    type Output;
-
-    extern "rust-call" fn call_box(self: Box<Self>, args: Args) -> Self::Output;
-}
-```
-
-For `Sized` types (from which we coerce into `dyn FnBox`), we define
-the blanket impl that proxies calls to `FnOnce`:
-
-```rust,ignore
-impl<A, F: FnOnce<A>> FnBox<A> for F {
-    type Output = <F as FnOnce<A>>::Output;
+The impls are parallel to these (relatively old) impls:
 
-    extern "rust-call" fn call_box(self: Box<Self>, args: A) -> Self::Output {
-        // Here we assume `F` to be sized.
-        <F as FnOnce<A>>::call_once(*self, args)
-    }
-}
-```
-
-Now it looks like that we can define `FnOnce` for `Box<F>`.
-
-```rust,ignore
-impl<A, F: FnBox<A> + ?Sized> FnOnce<A> for Box<F> {
-    type Output = <F as FnOnce<A>>::Output;
-
-    extern "rust-call" fn call_once(self, args: A) -> Self::Output {
-        <F as FnBox<A>>::call_box(self, args)
-    }
-}
-```
-
-## Limitations of `FnBox`
-
-### Interaction with HRTB
-
-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>`.
-
-```rust,ignore
-impl<'a, A, R> FnOnce<A> for Box<dyn FnBox<A, Output = R> + 'a> {
-    type Output = R;
-
-    extern "rust-call" fn call_once(self, args: A) -> Self::Output {
-        FnBox::call_box(*self, args)
-    }
-}
-
-// Sendable variant
-impl<'a, A, R> FnOnce<A> for Box<dyn FnBox<A, Output = R> + Send + 'a> {
-    type Output = R;
-
-    extern "rust-call" fn call_once(self, args: A) -> Self::Output {
-        FnBox::call_box(*self, args)
-    }
-}
-```
-
-The consequence is that the following example doesn't work:
-
-```rust,compile_fail
-#![feature(fnbox)]
-
-use std::boxed::FnBox;
-
-fn main() {
-    let f: Box<dyn FnBox(&i32)> = Box::new(|x| println!("{}", x));
-    f(42);
-}
-```
-
-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.
-
-### Interaction with `Fn`/`FnMut`
-
-It would be natural to have the following impls:
-
-```rust,ignore
-impl<A, F: FnMut<A> + ?Sized> FnMut<A> for Box<F> {
-    // ...
-}
-impl<A, F: Fn<A> + ?Sized> Fn<A> for Box<F> {
-    // ...
-}
-```
-
-However, we hadn't been able to write these in presense of `FnBox`
-(until [`boxed_closure_impls`][boxed_closure_impls] lands).
-
-To have `FnMut<A>` for `Box<F>`, we should have (at least) this impl:
-
-```rust,ignore
-// 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> for Box<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:
-
-```rust,ignore
-struct MyStruct;
-impl<'a> FnMut<MyStruct> for dyn FnBox<MyStruct, Output = ()> + 'a {
-    // ...
-}
+```rust
+impl<A, F> FnOnce for &mut F where F: FnMut<A> + ?Sized {}
+impl<A, F> FnMut for &mut F where F: FnMut<A> + ?Sized {}
+impl<A, F> Fn for &mut F where F: Fn<A> + ?Sized {}
+impl<A, F> FnOnce for &F where F: Fn<A> + ?Sized {}
+impl<A, F> FnMut for &F where F: Fn<A> + ?Sized {}
+impl<A, F> Fn for &F where F: Fn<A> + ?Sized {}
 ```
 
-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`][boxed_closure_impls],
-we now have a slightly modified version of `FnBox`:
+Before the introduction of [`unsized_locals`][unsized_locals], we had been unable to provide the former impls. That means, unlike `&dyn Fn()` or `&mut dyn FnMut()` we could not use `Box<dyn FnOnce()>` at that time.
 
-```rust,ignore
-// It's now a subtrait of `FnOnce`
-pub trait FnBox<Args>: FnOnce<Args> {
-    // now uses FnOnce::Output
-    // type Output;
-
-    extern "rust-call" fn call_box(self: Box<Self>, args: Args) -> Self::Output;
-}
-```
-
-## The future of `fnbox`
+[unsized_locals]: language-features/unsized-locals.html
 
-`FnBox` has long been considered a temporary solution for `Box<FnOnce>`
-problem. Since we have [`boxed_closure_impls`][boxed_closure_impls] now,
-it may be deprecated and removed in the future.
+`FnBox()` is an alternative approach to `Box<dyn FnBox()>` is delegated to `FnBox::call_box` which doesn't need unsized locals. As we now have `Box<dyn FnOnce()>` working, the `fnbox` feature is going to be removed.