如何从 Rust 中具有生命周期参数的结构上的方法将闭包传递给函数?

How to pass a closure to a function from a method on a struct that has a lifetime parameter in Rust?

提问人:Plegeus 提问时间:6/24/2023 最后编辑:Plegeus 更新时间:6/25/2023 访问量:52

问:

我有一个结构体 Foo,其生命周期参数为 'a:

struct Foo<'a> {
  /* snip */
}

我想将一个闭包(包含 Foo 的一些字段)传递给任意函数,例如:

impl Foo<'_> {

  // ...

  pub fn do_something(&mut self) {
    self.some_object.do_something(
      Box::new(
        |a_parameter| {
          self.some_other_object.do_something_else(a_parameter);
        }
      )
    );
  }

  // ...

}

do_something的定义如下:

fn do_something(f: Box<dyn Fn(T)>) { /* snip */ }

我得到的错误是

cast requires that `'1` must outlive `'static`

其中 '1 是在 Foo 本身上实现的函数中对 self 的可变引用的生存期。

我认为问题在于我需要指定闭包(在盒子内)的寿命与定义它的函数(即作用域)一样长。我只是不知道该怎么做,我尝试了很多方法,例如,为do_something添加生命周期参数,但没有成功。

谢谢!

编辑

一个(非工作)示例:

struct Bar {
  num: i32,
} 
impl Bar {
  fn do_something(&self, f: Box<dyn Fn(i32)>) {
    f(self.num);
  }
}

struct Foo<'a> {
  bar: &'a Bar,
  other_num: i32,
}

impl Foo<'_> {

  fn do_something(&self) {
    self.bar.do_something(
      Box::new(|n| {
        println!("{}", n + self.other_num);
      })
    );
  }

}


fn main() {
 
  let bar = Bar { num: 123, };
  let foo = Foo { bar: &bar, other_num: 456, };

  foo.do_something();

}
防锈 瓶盖 寿命

评论

1赞 isaactfa 6/24/2023
请提供一个最小的可重复示例
0赞 Plegeus 6/24/2023
是的,只是做到了。

答:

2赞 isaactfa 6/24/2023 #1

因为您将闭包作为特征对象 (),所以有一个隐式绑定 (),这意味着不允许闭包借用任何数据。但是您要传递的闭包借用了 .您可以使用显式生存期使参数更通用:Box<dyn Fn(i32)>+ 'staticBox<dyn Fn(i32) + 'static>&self

impl Bar {
    fn do_something<'a>(&self, f: Box<dyn Fn(i32) + 'a>) {
    //             ^^^^                          ^^^^^^
    // `f` is now allowed to borrow data for any lifetime `'a`
        f(self.num);
    }
}

可以省略

impl Bar {
    fn do_something(&self, f: Box<dyn Fn(i32) + '_>) {
        f(self.num);
    }
}

这是一个非常简单的例子。在更复杂的代码中,生存期要求可能比这更严格。

评论

0赞 Plegeus 6/24/2023
是的,这确实可以解决问题,太棒了。对盒子有什么想法吗?许多带有“lambda's”的高级语言将允许您简单地传递 |x|{ ... }并且没有任何终生的恶作剧。
0赞 isaactfa 6/24/2023
这实际上是您需要终身绑定的原因。如果有签名,它可以在没有注释的情况下工作。Boxdo_somethingfn do_something(&self, f: impl Fn(i32))
0赞 isaactfa 6/24/2023
而且你不需要关闭,避免了分配。Box
0赞 Plegeus 6/25/2023
嗯,好的,我明白了。我尝试做 impl Fn(...),但这似乎又打开了一整罐蠕虫。Foo 实际上应该是一个特征,zhich 会导致一个错误,即它不能被制作成一个对象(当存储为 &'a dyn Foo 时,也是因为应该有一个类型参数 T, Foo<T>)。无论如何,感谢您的帮助。:)