如何处理闭包中的生存期,将闭包作为参数和返回类型?

How to handle lifetimes in closures combined with closures as params and return types?

提问人:andreihondrari 提问时间:8/3/2023 最后编辑:Chayim Friedmanandreihondrari 更新时间:8/3/2023 访问量:27

问:

我尝试编写一个接收闭包(我们称之为 A)的闭包,该闭包返回一个接收值的闭包,然后将闭包 A 应用于它。

样本:

let do_some = |f: &dyn Fn(u32) -> u32| move |x: u32| f(x);
let result = do_some(&|v: u32| v * 1111)(7);

观察:

  • 闭包之所以是 A,是因为这是编译器让我将闭包传递给另一个闭包的唯一方式f&dyn Fn()
  • 最内层闭包上的移动运算符是为了避免被最内层闭包借用f

问题:

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`

当闭包不允许通过符号进行通用生存期规范时,如何指定生存期?<'a'>

防锈 瓶盖 寿命

评论


答:

0赞 Chayim Friedman 8/3/2023 #1

这是闭包中类型推断的一个怪癖。编译器认为闭包接受某个特定的生存期,但实际上你希望它占用任何生存期(HRTB)。这个事实导致它出错,原因我不会在这里解释(因为它们又长又复杂)。&'a dyn Fn'a&'a dyn Fn'a

在夜间,可以按如下方式“修复”错误的生命周期(是的,这很麻烦):

#![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) };

不幸的是,在稳定版上,据我所知,没有办法修复此错误。但是,首先可以通过使用 a 而不是引用来避免该问题:Box

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);

或者,您可以将返回的闭包装箱,并使用一个小的帮助函数来帮助编译器确定正确的生存期:

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);

我在这里解释了更多关于这个问题的信息。