如何在'选项中多次访问向量?

时间:2016-11-17 14:10:24

标签: rust borrow-checker

如何在没有Rust移动第一次访问的情况下访问选项中的向量?

fn maybe_push(v_option: Option<&mut Vec<usize>>) -> usize {
    let mut c = 0;
    if let Some(v) = v_option {
        for i in 0..10 {
            v.push(i);
            c += i;
        }
    } else {
        for i in 0..10 {
            c += i;
        }
    }

    // second access, fails
    if let Some(v) = v_option {
        for i in 10..20 {
            v.push(i);
            c += i;
        }
    } else {
        for i in 10..20 {
            c += i;
        }
    }

    return c;
}


fn main() {
    let mut v: Vec<usize> = vec![];

    println!("{}", maybe_push(Some(&mut v)));
    println!("{}", maybe_push(None));

    println!("{:?}", v);
}

这给出了错误:

error[E0382]: use of partially moved value: `v_option`
  --> src/main.rs:16:22
   |
4  |     if let Some(v) = v_option {
   |                 - value moved here
...
16 |     if let Some(v) = v_option {
   |                      ^^^^^^^^ value used here after move

使用已建议的if let Some(ref mut v) = v_option {也失败了:

error: cannot borrow immutable anonymous field `(v_option:std::prelude::v1::Some).0` as mutable
 --> src/main.rs:4:21
  |
4 |         if let Some(ref mut v) = v_option {
  |                     ^^^^^^^^^

error: cannot borrow immutable anonymous field `(v_option:std::prelude::v1::Some).0` as mutable
  --> src/main.rs:17:21
   |
17 |         if let Some(ref mut v) = v_option {
   |                     ^^^^^^^^^

1 个答案:

答案 0 :(得分:6)

按值匹配将值移动到模式变量中。移动使原始值不可用,除了实现Copy特征的非常简单的对象,例如数字。与C中的指针不同,可变引用不可复制,这可以在以下不编译的示例中看到:

let mut v = vec![1, 2, 3];
let rv = &mut v;  // mutable reference to v
{
    // move rv to r1, r1 now becomes the sole mutable reference to v
    let r1 = rv;
    r1.push(4);
}
{
    let r2 = rv;  // error: rv was already moved to r1
    r2.push(5);
}

Rust拒绝上述内容,因为它强制执行禁止对对象进行多个可变引用的一般规则。尽管这个特定的代码片段是安全的,但允许同时对同一个对象进行多个可变引用会使编写Rust明确设计用于防止的不安全程序变得容易,例如,包含多线程代码中的数据争用的那些,或通过无效的迭代器访问数据的那些。因此,作业let r1 = rv只能移动 rvr1的引用,不允许现在引用移动变量let r2 = rv的{​​{1}}语句}。

要修复代码,我们必须为引用创建单独的引用。这将创建对原始引用的两个不同的可变引用,而不是将原始可变引用移动到内部范围中:

rv

let mut v = vec![1, 2, 3]; let mut rv = &mut v; { // rr1 is a *new* mutable reference to rv - no move is performed let rr1 = &mut rv; rr1.push(4); } { // rr2 is a *separate* new mutable reference to rv - also no move let rr2 = &mut rv; rr2.push(5); } 调用的语法与pushr1.push相同,因为Rust的rr1.push运算符会自动取消引用任意数量的引用。

要返回问题中的示例,.中对Vec<usize>的引用与上面的Option引用类似,并使用v模式进行匹配引用Some(v)模式变量。

要修复它,必须按照上面的示例更改模式,以指定引用的变量,该变量本身就是一个引用。这是使用v语法实现的。如上所述,在if let Some(ref mut v)声明必须更改为可变的情况下,此修复还要求rv是可变的。通过这两个更改,代码编译:

Option

另一种可能性是使用fn maybe_push(mut v_option: Option<&mut Vec<usize>>) -> usize { let mut c = 0; if let Some(ref mut v) = v_option { for i in 0..10 { v.push(i); c += i; } } else { for i in 0..10 { c += i; } } if let Some(ref mut v) = v_option { for i in 10..20 { v.push(i); c += i; } } else { for i in 10..20 { c += i; } } return c; } fn main() { let mut v: Vec<usize> = vec![]; println!("{}", maybe_push(Some(&mut v))); println!("{}", maybe_push(None)); println!("{:?}", v); } 方法将选项内容作为对先前内容的可变引用返回。这在概念上等同于第一个代码段中从as_mut()let r1 = rv的更改,并允许使用let rr1 = &mut rv模式,其中if let Some(v)仍然是对mutable的引用参考向量。这也要求v被声明为可变。