由于在函数参数中也被借用为可变引用,因此无法作为不可变引用进行借用。

30

这里发生了什么(playground)?
struct Number {
    num: i32
}

impl Number {
    fn set(&mut self, new_num: i32) {
        self.num = new_num;
    }
    fn get(&self) -> i32 {
        self.num
    }
}

fn main() {
    let mut n = Number{ num: 0 };
    n.set(n.get() + 1);
}

给出以下错误:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
  --> <anon>:17:11
   |
17 |     n.set(n.get() + 1);
   |     -     ^          - mutable borrow ends here
   |     |     |
   |     |     immutable borrow occurs here
   |     mutable borrow occurs here

然而,如果您只是将代码更改为以下内容,则可以正常工作:
fn main() {
    let mut n = Number{ num: 0 };
    let tmp = n.get() + 1;
    n.set(tmp);
}

对我来说,它们看起来完全等价 - 我的意思是,我期望在编译过程中前者会被转换为后者。Rust不会在评估上一级函数调用之前评估所有函数参数吗?

你可以在这里找到借用检查过程的详细信息。 - ljedrz
1
有一个unborrow宏可以用来解决问题,并且在reddit上有相关讨论。 - wimh
1
请查看此博客文章。它深入解释了这个问题。还有后续文章提供更多关于该问题的信息。 - aochagavia
1
相关链接:https://github.com/rust-lang/rfcs/pull/2094 - Claudia
1个回答

34
这行:

n.set(n.get() + 1);

被展开为

Number::set(&mut n, n.get() + 1);

错误信息现在可能会更清晰:
error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
  --> <anon>:18:25
   |
18 |     Number::set(&mut n, n.get() + 1);
   |                      -  ^          - mutable borrow ends here
   |                      |  |
   |                      |  immutable borrow occurs here
   |                      mutable borrow occurs here

由于 Rust 从左到右评估参数,因此该代码等同于以下代码:
let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);

编辑注:这个代码示例可以直观地感受到底层问题,但并不完全准确。即使使用非词法生命周期,扩展后的代码仍然失败,但原始代码可以编译。有关问题的完整描述,请查看借用检查器原始实现中的评论

现在很明显出了什么问题。交换那前两行就可以解决这个问题,但 Rust 并不进行这种控制流分析。

这最初是作为bug #6268创建的,现在已整合到RFC 2094非词法生命周期中。如果您使用 Rust 1.36 或更高版本,则 NLL 会自动启用,您的代码将无错误编译
另请参见:

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