如果向量为空,如何在插入值时避免向量的多个可变借位?

时间:2017-02-15 21:09:11

标签: rust borrow-checker

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

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

1 个答案:

答案 0 :(得分:6)

你没有。好吧,你“避免”多次借款......没有多次借款。

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

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

更具有惯用力:

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

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

  

取一个可用于两种情况的可变借用

这不是它的工作原理。你必须了解记忆是如何发挥作用的,以及参考是什么。 Vec包含指向存储数据的内存的指针。

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

issue you linked和相关pre-RFC的全部内容是此代码能够被确定为安全:

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

在这种情况下,程序员可以看到我们从不在None情况下使用借位,因此编译器理论上可以在执行任何匹配臂之前结束借用,否则会使两个臂脱离尊重生命。 现在不这样做

但是,在您的代码版本中,实际上更糟。通过明确地借入并将其保存在变量中,您可以延长借入需要保留多长时间,从而迫使它们重叠。

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

另见: