如果向量为空,插入值时如何避免多次可变借用向量?

How to avoid multiple mutable borrows of a vector when inserting a value if the vector is empty?

this github discussion 中,您会发现这段引起借用检查器愤怒的代码:

fn main() {
    let mut vec = vec!();

    match vec.first() {
        None => vec.push(5),
        Some(v) => unreachable!(),
    }
}

我理解为什么在不可变借用未解决的情况下进行突变是有问题的。我假设一个解决方案是明确地只借一个(一个可变的),但它仍然导致我有两个借用,一个不可变的借用,然后一个可变的借用:

fn main() {
    let mut vec: Vec<i32> = vec!();

    let r_vec: &mut Vec<i32> = &mut vec;

    match r_vec.first() {
       None => r_vec.push(5),
       Some(v) => unreachable!(),
   }   
} 

编译器仍然不高兴:

error[E0502]: cannot borrow `*r_vec` as mutable because it is also borrowed as immutable
 --> testrust.rs:7:17
  |
6 |     match r_vec.first() {
  |           ----- immutable borrow occurs here
7 |         None => r_vec.push(5),
  |                 ^^^^^ mutable borrow occurs here
8 |         Some(v) => unreachable!(),
9 |     }
  |     - immutable borrow ends here

为什么我的解决方法不起作用,解决此问题的正确方法是什么?

你不知道。好吧,你 "avoid" 有多次借用......没有多次借用。

fn main() {
    let mut vec = vec![];

    if vec.first().is_none() {
        vec.push(5);
    }
}

更地道:

if vec.is_empty() {
    vec.push(5);
}

在这两种情况下,我们借用 vec 来进行方法调用,但在执行 if 的主体之前终止借用。将其与 match 进行比较,其中在匹配头表达式中进行借用,然后与匹配臂共享。


take one mutable borrow that can be used for both cases

事情不是这样的。您必须了解记忆如何发挥作用以及参考是什么。 Vec 包含指向存储数据的内存的指针。

当您获得对数据向量的引用时,该引用保存数据的内存地址,并且编译器确保只有其中一个允许改变 Vec。当你push一个值时,那可能需要分配新的内存来存储所有的数据。这可能会使您持有的参考无效。如果发生这种情况,那么下次您使用该引用时,它会指向其他一些不相关的内存,您的程序会崩溃,您的用户数据会暴露于安全漏洞等,等等。


issue you linked and the related pre-RFC 的全部要点是此代码应该 能够被确定为安全的:

match vec.first() {
    None => vec.push(5),
    Some(v) => unreachable!(),
}

在这种情况下,程序员可以看到我们从不在 None 情况下使用借用,因此编译器理论上可以在执行任何匹配臂之前结束借用,或者以其他方式使两个臂关于生命周期不相交。它现在不这样做

但是,在您的代码版本中,它实际上更糟。通过显式获取借用并将其保存在变量中,您可以延长借用需要保留的时间,迫使它们重叠。

目前,唯一的解决办法是重新排序代码以人为地限制借用。我在实践中并没有发现这很烦人,因为通常会有更好的代码组织方式。

另请参阅: