diff --git a/src/doc/unstable-book/src/library-features/fnbox.md b/src/doc/unstable-book/src/library-features/fnbox.md index 3200601e557f6..16ebab3abeb50 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` 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` instead of `Box`. +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 String> = Box::new(|| resource); - - // Call it - let s = f(); - println!("{}", s); -} +impl FnOnce for Box where F: FnOnce + ?Sized {} +impl FnMut for Box where F: FnMut + ?Sized {} +impl Fn for Box where F: Fn + ?Sized {} ``` -## How `Box` did not work - -**Spoiler**: [`boxed_closure_impls`][boxed_closure_impls] actually implements -`Box`! 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` would look like: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::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 { - 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, args: Args) -> Self::Output; -} -``` - -...and then, modify the `impl` like this: - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - // We can use `call_box` here! - >::call_box(self, args) - } - // We'll have to define this in every impl of `FnOnce`. - extern "rust-call" fn call_box(self: Box, args: A) -> Self::Output { - >::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 { - type Output; - - extern "rust-call" fn call_box(self: Box, 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> FnBox for F { - type Output = >::Output; +The impls are parallel to these (relatively old) impls: - extern "rust-call" fn call_box(self: Box, args: A) -> Self::Output { - // Here we assume `F` to be sized. - >::call_once(*self, args) - } -} -``` - -Now it looks like that we can define `FnOnce` for `Box`. - -```rust,ignore -impl + ?Sized> FnOnce for Box { - type Output = >::Output; - - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::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`, `liballoc` only -implements `FnOnce` for `Box`. - -```rust,ignore -impl<'a, A, R> FnOnce for Box + '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 for Box + 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 = 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` or -`dyn FnBox + Send + 'a` due to HRTB. - -### Interaction with `Fn`/`FnMut` - -It would be natural to have the following impls: - -```rust,ignore -impl + ?Sized> FnMut for Box { - // ... -} -impl + ?Sized> Fn for Box { - // ... -} -``` - -However, we hadn't been able to write these in presense of `FnBox` -(until [`boxed_closure_impls`][boxed_closure_impls] lands). - -To have `FnMut` for `Box`, we should have (at least) this impl: - -```rust,ignore -// Note here we only impose `F: FnMut`. -// If we can write `F: FnOnce` here, that will resolve all problems. -impl + ?Sized> FnOnce for Box { - // ... -} -``` - -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 for dyn FnBox + 'a { - // ... -} +```rust +impl FnOnce for &mut F where F: FnMut + ?Sized {} +impl FnMut for &mut F where F: FnMut + ?Sized {} +impl Fn for &mut F where F: Fn + ?Sized {} +impl FnOnce for &F where F: Fn + ?Sized {} +impl FnMut for &F where F: Fn + ?Sized {} +impl Fn for &F where F: Fn + ?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` at that time. -```rust,ignore -// It's now a subtrait of `FnOnce` -pub trait FnBox: FnOnce { - // now uses FnOnce::Output - // type Output; - - extern "rust-call" fn call_box(self: Box, 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` -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` is delegated to `FnBox::call_box` which doesn't need unsized locals. As we now have `Box` working, the `fnbox` feature is going to be removed.