@@ -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