Skip to content

Commit 440e873

Browse files
qnighycrlf0710
authored andcommitted
Simplify fnbox docs.
1 parent ecc3e89 commit 440e873

File tree

1 file changed

+15
-235
lines changed
  • src/doc/unstable-book/src/library-features

1 file changed

+15
-235
lines changed

src/doc/unstable-book/src/library-features/fnbox.md

+15-235
Original file line numberDiff line numberDiff line change
@@ -6,247 +6,27 @@ The tracking issue for this feature is [#28796]
66

77
------------------------
88

9-
As an analogy to `&dyn Fn()` and `&mut dyn FnMut()`, you may have expected
10-
`Box<dyn FnOnce()>` to work. But it hadn't until the recent improvement!
11-
`FnBox` had been a **temporary** solution for this until we are able to pass
12-
trait objects by value.
13-
14-
See [`boxed_closure_impls`][boxed_closure_impls] for the newer approach.
15-
16-
[boxed_closure_impls]: library-features/boxed-closure-impls.html
17-
18-
## Usage
19-
20-
If you want to box `FnOnce` closures, you can use `Box<dyn FnBox()>` instead of `Box<dyn FnOnce()>`.
9+
This had been a temporary alternative to the following impls:
2110

2211
```rust
23-
#![feature(fnbox)]
24-
25-
use std::boxed::FnBox;
26-
27-
fn main() {
28-
let resource = "hello".to_owned();
29-
// Create a boxed once-callable closure
30-
let f: Box<dyn FnBox() -> String> = Box::new(|| resource);
31-
32-
// Call it
33-
let s = f();
34-
println!("{}", s);
35-
}
12+
impl<A, F> FnOnce for Box<F> where F: FnOnce<A> + ?Sized {}
13+
impl<A, F> FnMut for Box<F> where F: FnMut<A> + ?Sized {}
14+
impl<A, F> Fn for Box<F> where F: Fn<A> + ?Sized {}
3615
```
3716

38-
## How `Box<dyn FnOnce()>` did not work
39-
40-
**Spoiler**: [`boxed_closure_impls`][boxed_closure_impls] actually implements
41-
`Box<dyn FnOnce()>`! This didn't work because we lacked features like
42-
[`unsized_locals`][unsized_locals] for a long time. Therefore, this section
43-
just explains historical reasons for `FnBox`.
44-
45-
[unsized_locals]: language-features/unsized-locals.html
46-
47-
### First approach: just provide `Box` adapter impl
48-
49-
The first (and natural) attempt for `Box<dyn FnOnce()>` would look like:
50-
51-
```rust,ignore
52-
impl<A, F: FnOnce<A> + ?Sized> FnOnce<A> for Box<F> {
53-
type Output = <F as FnOnce<A>>::Output;
54-
55-
extern "rust-call" fn call_once(self, args: A) -> Self::Output {
56-
<F as FnOnce<A>>::call_once(*self, args)
57-
}
58-
}
59-
```
60-
61-
However, this doesn't work. We have to relax the `Sized` bound for `F` because
62-
we expect trait objects here, but `*self` must be `Sized` because it is passed
63-
as a function argument.
64-
65-
### The second attempt: add `FnOnce::call_box`
66-
67-
One may come up with this workaround: modify `FnOnce`'s definition like this:
68-
69-
```rust,ignore
70-
pub trait FnOnce<Args> {
71-
type Output;
72-
73-
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
74-
// Add this new method
75-
extern "rust-call" fn call_box(self: Box<Self>, args: Args) -> Self::Output;
76-
}
77-
```
78-
79-
...and then, modify the `impl` like this:
80-
81-
```rust,ignore
82-
impl<A, F: FnOnce<A> + ?Sized> FnOnce<A> for Box<F> {
83-
type Output = <F as FnOnce<A>>::Output;
84-
85-
extern "rust-call" fn call_once(self, args: A) -> Self::Output {
86-
// We can use `call_box` here!
87-
<F as FnOnce<A>>::call_box(self, args)
88-
}
89-
// We'll have to define this in every impl of `FnOnce`.
90-
extern "rust-call" fn call_box(self: Box<Self>, args: A) -> Self::Output {
91-
<F as FnOnce<A>>::call_box(*self, args)
92-
}
93-
}
94-
```
95-
96-
What's wrong with this? The problem here is crates:
97-
98-
- `FnOnce` is in `libcore`, as it shouldn't depend on allocations.
99-
- `Box` is in `liballoc`, as it:s the very allocated pointer.
100-
101-
It is impossible to add `FnOnce::call_box` because it is reverse-dependency.
102-
103-
There's another problem: `call_box` can't have defaults.
104-
`default impl` from the specialization RFC may resolve this problem.
105-
106-
### The third attempt: add `FnBox` that contains `call_box`
107-
108-
`call_box` can't reside in `FnOnce`, but how about defining a new trait in
109-
`liballoc`?
110-
111-
`FnBox` is almost a copy of `FnOnce`, but with `call_box`:
112-
113-
```rust,ignore
114-
pub trait FnBox<Args> {
115-
type Output;
116-
117-
extern "rust-call" fn call_box(self: Box<Self>, args: Args) -> Self::Output;
118-
}
119-
```
120-
121-
For `Sized` types (from which we coerce into `dyn FnBox`), we define
122-
the blanket impl that proxies calls to `FnOnce`:
123-
124-
```rust,ignore
125-
impl<A, F: FnOnce<A>> FnBox<A> for F {
126-
type Output = <F as FnOnce<A>>::Output;
17+
The impls are parallel to these (relatively old) impls:
12718

128-
extern "rust-call" fn call_box(self: Box<Self>, args: A) -> Self::Output {
129-
// Here we assume `F` to be sized.
130-
<F as FnOnce<A>>::call_once(*self, args)
131-
}
132-
}
133-
```
134-
135-
Now it looks like that we can define `FnOnce` for `Box<F>`.
136-
137-
```rust,ignore
138-
impl<A, F: FnBox<A> + ?Sized> FnOnce<A> for Box<F> {
139-
type Output = <F as FnOnce<A>>::Output;
140-
141-
extern "rust-call" fn call_once(self, args: A) -> Self::Output {
142-
<F as FnBox<A>>::call_box(self, args)
143-
}
144-
}
145-
```
146-
147-
## Limitations of `FnBox`
148-
149-
### Interaction with HRTB
150-
151-
Firstly, the actual implementation is different from the one presented above.
152-
Instead of implementing `FnOnce` for `Box<impl FnBox>`, `liballoc` only
153-
implements `FnOnce` for `Box<dyn FnBox>`.
154-
155-
```rust,ignore
156-
impl<'a, A, R> FnOnce<A> for Box<dyn FnBox<A, Output = R> + 'a> {
157-
type Output = R;
158-
159-
extern "rust-call" fn call_once(self, args: A) -> Self::Output {
160-
FnBox::call_box(*self, args)
161-
}
162-
}
163-
164-
// Sendable variant
165-
impl<'a, A, R> FnOnce<A> for Box<dyn FnBox<A, Output = R> + Send + 'a> {
166-
type Output = R;
167-
168-
extern "rust-call" fn call_once(self, args: A) -> Self::Output {
169-
FnBox::call_box(*self, args)
170-
}
171-
}
172-
```
173-
174-
The consequence is that the following example doesn't work:
175-
176-
```rust,compile_fail
177-
#![feature(fnbox)]
178-
179-
use std::boxed::FnBox;
180-
181-
fn main() {
182-
let f: Box<dyn FnBox(&i32)> = Box::new(|x| println!("{}", x));
183-
f(42);
184-
}
185-
```
186-
187-
Note that `dyn FnBox(&i32)` desugars to
188-
`dyn for<'r> FnBox<(&'r i32,), Output = ()>`.
189-
It isn't covered in `dyn FnBox<A, Output = R> + 'a` or
190-
`dyn FnBox<A, Output = R> + Send + 'a` due to HRTB.
191-
192-
### Interaction with `Fn`/`FnMut`
193-
194-
It would be natural to have the following impls:
195-
196-
```rust,ignore
197-
impl<A, F: FnMut<A> + ?Sized> FnMut<A> for Box<F> {
198-
// ...
199-
}
200-
impl<A, F: Fn<A> + ?Sized> Fn<A> for Box<F> {
201-
// ...
202-
}
203-
```
204-
205-
However, we hadn't been able to write these in presense of `FnBox`
206-
(until [`boxed_closure_impls`][boxed_closure_impls] lands).
207-
208-
To have `FnMut<A>` for `Box<F>`, we should have (at least) this impl:
209-
210-
```rust,ignore
211-
// Note here we only impose `F: FnMut<A>`.
212-
// If we can write `F: FnOnce<A>` here, that will resolve all problems.
213-
impl<A, F: FnMut<A> + ?Sized> FnOnce<A> for Box<F> {
214-
// ...
215-
}
216-
```
217-
218-
Unfortunately, the compiler complains that it **overlaps** with our
219-
`dyn FnBox()` impls. At first glance, the overlap must not happen.
220-
The `A` generic parameter does the trick here: due to coherence rules,
221-
a downstream crate may define the following impl:
222-
223-
```rust,ignore
224-
struct MyStruct;
225-
impl<'a> FnMut<MyStruct> for dyn FnBox<MyStruct, Output = ()> + 'a {
226-
// ...
227-
}
19+
```rust
20+
impl<A, F> FnOnce for &mut F where F: FnMut<A> + ?Sized {}
21+
impl<A, F> FnMut for &mut F where F: FnMut<A> + ?Sized {}
22+
impl<A, F> Fn for &mut F where F: Fn<A> + ?Sized {}
23+
impl<A, F> FnOnce for &F where F: Fn<A> + ?Sized {}
24+
impl<A, F> FnMut for &F where F: Fn<A> + ?Sized {}
25+
impl<A, F> Fn for &F where F: Fn<A> + ?Sized {}
22826
```
22927

230-
The trait solver doesn't know that `A` is always a tuple type, so this is
231-
still possible. With this in mind, the compiler emits the overlap error.
232-
233-
## Modification
234-
235-
For compatibility with [`boxed_closure_impls`][boxed_closure_impls],
236-
we now have a slightly modified version of `FnBox`:
28+
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.
23729

238-
```rust,ignore
239-
// It's now a subtrait of `FnOnce`
240-
pub trait FnBox<Args>: FnOnce<Args> {
241-
// now uses FnOnce::Output
242-
// type Output;
243-
244-
extern "rust-call" fn call_box(self: Box<Self>, args: Args) -> Self::Output;
245-
}
246-
```
247-
248-
## The future of `fnbox`
30+
[unsized_locals]: language-features/unsized-locals.html
24931

250-
`FnBox` has long been considered a temporary solution for `Box<FnOnce>`
251-
problem. Since we have [`boxed_closure_impls`][boxed_closure_impls] now,
252-
it may be deprecated and removed in the future.
32+
`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.

0 commit comments

Comments
 (0)