@@ -6,247 +6,27 @@ The tracking issue for this feature is [#28796]
6
6
7
7
------------------------
8
8
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:
21
10
22
11
``` 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 {}
36
15
```
37
16
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:
127
18
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 {}
228
26
```
229
27
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.
237
29
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
249
31
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