I've tried writing a closure that receives a closure (let's call it A) that returns a closure that receives a value and then applies closure A to it.
Sample:
let do_some = |f: &dyn Fn(u32) -> u32| move |x: u32| f(x);
let result = do_some(&|v: u32| v * 1111)(7);
Observation:
f
is a &dyn Fn()
is because it is the only way the compiler would let me pass a closure to another closuref
being borrowed by the innermost closureProblem:
error: lifetime may not live long enough
--> src/main.rs:2:44
|
2 | let do_some = |f: &dyn Fn(u32) -> u32| move |x: u32| f(x);
| - - ^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure `[closure@src/main.rs:2:44: 2:57]` contains a lifetime `'2`
| let's call the lifetime of this reference `'1`
How to specify lifetime when closures don't allow generic lifetime specification via thee <'a'>
notation?
This is a quirk of type inference in closures. The compiler thinks the closure accepts &'a dyn Fn
with some specific lifetime 'a
, but you actually want it to take &'a dyn Fn
for any lifetime 'a
(HRTB). This fact causes it to error for reasons I won't explain here (because they're long and complicated).
On nightly, it is possible to "fix" the wrong lifetime as follows (yep, this is cumbersome):
#![feature(closure_lifetime_binder, type_alias_impl_trait)]
type RetFn<'a> = impl Fn(u32) -> u32 + 'a;
let do_some = for<'a> |f: &'a dyn Fn(u32) -> u32| -> RetFn<'a> { move |x: u32| f(x) };
On stable, unfortunately, as far as I know there is no way to fix this error. But you can avoid the problem in the first place by using a Box
instead of a reference:
let do_some = |f: Box<dyn Fn(u32) -> u32>| move |x: u32| f(x);
let result = do_some(Box::new(|v: u32| v * 1111))(7);
Alternatively, you can box the returned closure, and use a little helper function to help the compiler figure the right lifetime:
fn force_hrtb<F: Fn(&dyn Fn(u32) -> u32) -> Box<dyn Fn(u32) -> u32 + '_>>(f: F) -> F {
f
}
let do_some = force_hrtb(|f| Box::new(move |x: u32| f(x)));
let result = do_some(&|v: u32| v * 1111)(7);