如何使用这段简单代码满足Rust借用检查器的要求?

6
我从借用检查器中获得了一个Rust编译错误,并不知道如何解决。
下面的代码很简单,在C++中类似的代码没有问题。
fn main() {
    let mut nums = vec![1, 2, 3];
    if let Some(x) = nums.last() {
        nums.push(*x);
    }
}

以下是错误内容:

message: 'cannot borrow `nums` as mutable because it is also borrowed as immutable (4, 9)'

1
不了解Rust的话,可能不喜欢在nums.push()的同一语句中使用nums.last(),因为当向nums推送新条目时,nums.last()的值可能会发生改变。 - jwenting
2个回答

7
当您调用.last()时,您会将nums作为不可变的借用,因为对其进行突变将使您持有的引用x失效。然后,您调用.push,它将nums作为可变的借用。
问题在于,您现在同时拥有相同值的不可变和可变借用,这违反了Rust的内存安全保证(多个读取器或一个单一编写器保证您永远不会在任何地方拥有无效的内存)。
fn main() {
    let mut nums = vec![1, 2, 3];
    if let Some(x) = nums.last() { // Immutable borrow starts here
        nums.push(*x);             // Mutable borrow starts here
    }                              // Immutable and mutable borrows end here
}

解决方法是立即丢弃其结果的引用,从而降低不可变借用的范围,就像 @DanielSanchez 的示例一样:
let mut nums = vec![1, 2, 3];
if let Some(&x) = nums.last() { // Immutable borrow starts and ends here
    nums.push(x);               // Mutable borrow starts here
}                               // Mutable borrow ends here

谢谢,这个答案更清晰了。我只是不知道为什么不可变借用的生命周期没有结束。现在我知道原因是nums.last()返回的是引用。但是为什么"&x"可以结束不可变借用呢? - LiLei
2
在这个上下文中,&x 实际上是解引用 x。通常情况下,这会移动 x,但整数实现了 Copy trait,这意味着我们不是移动 x 而是使用一个自动生成的 x 的副本。我们对 x 进行解引用会销毁 .last() 返回的引用并结束它所引起的借用。 - Jan Hohenheim

5
您可以在守卫语句中取消引用引用:
fn main() {
    let mut nums = vec![1, 2, 3];
    if let Some(&x) = nums.last() {
        nums.push(x);
    }
}

Rust拥有强大的模式匹配功能,只要知道类型,就可以解开几乎所有东西的包装。请查看Rust模式匹配文档

谢谢,它有效。我以为 nums.last() 与 nums.push() 冲突了。Rust 的文档在哪里详细解释了“引用展开”? - LiLei
@LiLei,我更新了答案,你应该看一下rust拥有的所有模式匹配功能(甜如蜜糖);) - Netwave

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