可变地借用一个结构字段,同时在闭包中借用另一个

Posted

技术标签:

【中文标题】可变地借用一个结构字段,同时在闭包中借用另一个【英文标题】:Mutably borrow one struct field while borrowing another in a closure 【发布时间】:2016-07-22 14:42:22 【问题描述】:

我有一个包含两个字段的结构,我想使用另一个字段(不可变借用)修改一个字段(可变借用),但借用检查器出现错误。

比如下面的代码:

struct Struct 
    field1: Vec<i32>,
    field2: Vec<i32>,


fn main() 
    let mut strct = Struct 
        field1: vec![1, 2, 3],
        field2: vec![2, 3, 4],
    ;

    strct.field1.retain(|v| !strct.field2.contains(v));

    println!(":?", strct.field1);

给出以下错误:

error[E0502]: cannot borrow `strct.field1` as mutable because it is also borrowed as immutable
  --> src/main.rs:12:5
   |
12 |     strct.field1.retain(|v| !strct.field2.contains(v));
   |     ^^^^^^^^^^^^^------^---^^-----^^^^^^^^^^^^^^^^^^^^
   |     |            |      |    |
   |     |            |      |    first borrow occurs due to use of `strct` in closure
   |     |            |      immutable borrow occurs here
   |     |            immutable borrow later used by call
   |     mutable borrow occurs here

在闭包中使用另一个字段更新一个字段的 Rust 方法是什么?

【问题讨论】:

【参考方案1】:

通常借用检查器可以区分结构的不同字段,但这在闭包(lambdas)中不起作用。

相反,借用闭包外的第二个字段:

let field2 = &strct.field2;
strct.field1.retain(|v| !field2.contains(v));

【讨论】:

Proposed RFC 2229 ("Closures Capture Disjoint Fields") 将使闭包的捕获语义更加智能,但它需要大量的工作和思考。【参考方案2】:

这个recent blog post 显示了解决这类问题的一个非常有用的模式:

有时,当我想要非常精确时,我会以一种风格化的方式编写闭包,使它们捕获的内容一目了然。我没有写|v| ...,而是首先介绍一个创建大量局部变量的块,块中的最后一个东西是move闭包(move闭包拥有它们使用的东西,而不是借用它们来自创作者)。这可以完全控制借用的内容和方式。在这种情况下,闭包可能如下所示:

换句话说,借用是在闭包中正确定义的,并被移动到闭包中。这完全清楚地表明,它们的目的是为闭包提供借来的值。在原始问题的上下文中,模式如下所示:

strct.field1.retain(
    let field2 = &strct.field2;
    move |v| !field2.contains(v)
);

这段代码的一个很好的特性是,field2 的借用在不再使用后不会继续存在。

【讨论】:

现在我们有非词法生命周期。

以上是关于可变地借用一个结构字段,同时在闭包中借用另一个的主要内容,如果未能解决你的问题,请参考以下文章

如何借用一个结构的库函数字段?

为啥通过借用 u32 可以使闭包比当前函数寿命更长?

不能将不可变的'Box'内容借用为可变的

如何在 Rust 中修复“.. 在循环的上一次迭代中可变地借用”?

为啥 &mut self 允许借用 struct 成员,但不允许将 self 借用到不可变方法?

闭包中的Swift可变结构和结构的行为不同