【问题标题】:Could not work out a closure returned from a method capturing a member variable无法计算出从捕获成员变量的方法返回的闭包
【发布时间】:2019-12-12 07:09:32
【问题描述】:

我正在尝试为一个结构实现一个方法,该结构返回一个闭包move-捕获其成员之一。理由是该成员是Copy,因此闭包可以复制它并且不应该与结构生命周期相关联。但是,它不会编译。

我希望 Rust 不会坚持 copy 引用 &self ,并通过保持对结构的引用将闭包生命周期与结构生命周期联系起来。当它没有编译时,我预计错误消息中会出现这种情况,但是 - 有点令人惊讶 - 我不确定这是确切的问题。
限制闭包的匿名生命周期是函数生命周期,它是 &self 的生命周期,而不是 self 的生命周期。当我move 时,我真的不明白这是什么问题。

其实我有两个问题:
首先,为什么函数生命周期会限制我的闭包? (可能是对我这边的错误消息的误解,我很乐意指出)。
第二,如何强制结构成员的move 而不将其复制到temp 变量(就在返回闭包之前)并使用temp 在闭包内部(有效,但在我看来不优雅)。

这是我的代码的简化版本:

struct Struct {
    member: usize
}

impl Struct {
    fn func(&self) -> Box<dyn Fn(usize)->usize> {
        Box::new(move |argument: usize| {
            self.member + argument
        })
    }
}

fn main() {
    let s = Struct { member: 4 };
    println!("{}", s.func()(5));
}

这在编译时给出:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src\main.rs:7:18
  |
7 |           Box::new(move |argument: usize| {
  |  __________________^
8 | |             self.member + argument
9 | |         })
  | |_________^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:5...
 --> src\main.rs:6:5
  |
6 | /     fn func(&self) -> Box<dyn Fn(usize)->usize> {
7 | |         Box::new(move |argument: usize| {
8 | |             self.member + argument
9 | |         })
10| |     }
  | |_____^
  = note: ...so that the types are compatible:
          expected &Struct
             found &Struct
  = note: but, the lifetime must be valid for the static lifetime...
  = note: ...so that the expression is assignable:
          expected std::boxed::Box<(dyn std::ops::Fn(usize) -> usize + 'static)>
             found std::boxed::Box<dyn std::ops::Fn(usize) -> usize>

error: aborting due to previous error

【问题讨论】:

  • 请注意,在闭包中使用self.member将使用调用闭包时member的值,而使用临时变量将使用调用@987654335时的成员值@。这是一个很大的语义差异,编译器不允许更改它,只是为了避免添加一行代码来创建临时代码。
  • @mcarton 想一想:据我所知,Rust 所有权规则禁止对不可变引用的数据进行突变。这不能阻止两者之间的语义差异吗?无论如何,我明白了。但是我没有办法向move会员本身发出信号吗?

标签: rust closures move-semantics lifetime borrowing


【解决方案1】:

第一,为什么函数闭包会限制我的闭包?

在方法内部,self 实际上是一个引用,因为它在方法签名中被声明为&amp;self。在 move 闭包中使用引用将移动引用,但不会移动基础值。

您看到的直接错误是因为从该方法返回的Box 包含对结构的引用,但可能比结构寿命更长。如果允许,闭包可以访问一个悬空指针,这是 Rust 所禁止的。

第二,如何强制移动结构成员而不将其复制到临时变量

为了避免在闭包中引用self,这是你必须做的。 复制(或克隆)一个结构成员,您不能移动它 - 否则该结构将不完整。这在 Rust 中是一件完全正常的事情。例如:

impl Struct {
    fn func(&self) -> Box<dyn Fn(usize) -> usize> {
        let temp = self.member;
        Box::new(move |argument| {
            temp + argument
        })
    }
}

如果您在调用func 后不再需要该结构,那么您可以按值取self:

impl Struct {
    fn func(self) -> Box<dyn Fn(usize) -> usize> {
        Box::new(move |argument| {
            self.member + argument
        })
    }
}

这是可行的,因为self 已移入闭包中。但是,它不再可以在其他任何地方使用。

【讨论】:

    猜你喜欢
    • 2019-04-01
    • 2020-01-16
    • 2022-11-19
    • 2015-04-30
    • 2016-02-13
    • 1970-01-01
    • 2016-03-20
    • 1970-01-01
    • 2020-10-14
    相关资源
    最近更新 更多