如何从具有结构体引用的方法中返回一个装箱的闭包?

4

我有一个包含值的结构,并且我想获得一个可以对这个值进行操作的函数:

struct Returner {
    val: i32,
}

impl<'a> Returner {
    fn get(&'a self) -> Box<Fn(i32) -> i32> {
        Box::new(|x| x + self.val)
    }
}

这段代码编译失败:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:7:18
  |
7 |         Box::new(|x| x + self.val)
  |                  ^^^^^^^^^^^^^^^^
  |
note: first, the lifetime cannot outlive the lifetime 'a as defined on the impl at 5:1...
 --> src/main.rs:5:1
  |
5 | impl<'a> Returner {
  | ^^^^^^^^^^^^^^^^^
  = note: ...so that the types are compatible:
          expected &&Returner
             found &&'a Returner
  = note: but, the lifetime must be valid for the static lifetime...
  = note: ...so that the expression is assignable:
          expected std::boxed::Box<std::ops::Fn(i32) -> i32 + 'static>
             found std::boxed::Box<std::ops::Fn(i32) -> i32>

这是因为闭包借用了self,这对我来说没问题,因为我不打算在结构体被销毁后使用获得的函数。据我所知,有两种方法可以实现:
  1. 使用move关键字。但我不想使用它,因为它会占有该对象,并且我想在返回此函数后继续使用它。

  2. 显式指定闭包的生命周期,以告诉编译器它与调用它的结构体具有相同的生命周期。

我认为在我的情况下,第二种方式是正确的,但我还没有找到如何指定闭包生命周期的方法。是否有直接的方法可以做到这一点,或者我完全搞错了,这与Rust生命周期逻辑相矛盾?
2个回答

9
一般来说,您可以通过编写Box<Trait + 'a>来指定装箱的Trait对象的生命周期,并类似地为其他指针后面的Trait对象指定(如果省略,至少在Box的情况下,默认为'static)。因此,在这种特定情况下,您需要返回类型Box<(Fn(i32) -> i32) + 'a>
但是,当你这样做时,你会看到另一个错误,关于self的寿命不够长。原因是(没有move)闭包将捕捉对局部变量self的引用。解决方案是使用move。这不会移动Returner对象,而是移动作为Returner对象引用的self。
总之:
struct Returner {
    val: i32,
}

impl<'a> Returner {
    fn get(&'a self) -> Box<Fn(i32) -> i32 + 'a> {
        Box::new(move |x| x + self.val)
    }
}

1
是我还是这个<'a>应该修饰get而不是impl - Matthieu M.
1
@MatthieuM。你说得对,应该这样做。尽管在这里并没有发生任何不同,因为'a 在其他地方并没有出现,但这确实是错误的量词使用位置。 - user395760
@ Matthieu M. 我已经检查了两种方法,结果是相同的。 - Month

3

现有回答中所述

  1. 添加一个将self的生命周期绑定到返回值生命周期的lifetime。
  2. 将对self的引用移动到闭包内部。

自Rust 1.26以来,如果只返回单个类型,则不再需要返回一个boxed closure。相反,您可以使用impl Trait

impl Returner {
    fn get<'a>(&'a self) -> impl Fn(i32) -> i32 + 'a {
        move |x| x + self.val
    }
}

另请参阅:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接