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

ide*_*n42 1 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);
}
Run Code Online (Sandbox Code Playgroud)

这给出了错误:

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
Run Code Online (Sandbox Code Playgroud)

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 {
   |                     ^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

use*_*342 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);
}
Run Code Online (Sandbox Code Playgroud)

Rust拒绝上述内容,因为它强制执行禁止对对象进行多个可变引用的一般规则.尽管这个特定的代码片段是安全的,允许同时对同一个对象进行多个可变引用,这样可以很容易地编写Rust明确设计用来防止的那种不安全的程序,例如那些包含多线程代码中的数据争用的程序,或那些通过无效的迭代器访问数据.因此,分配let r1 = rv只能移动rv参考r1,则不允许let r2 = rv它现在是指移动变量声明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);
}
Run Code Online (Sandbox Code Playgroud)

语法push调用是相同的r1.push,并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);
}
Run Code Online (Sandbox Code Playgroud)

另一种可能性是使用该as_mut()方法将选项内容作为对先前内容的可变引用返回.这在概念上等同于从第一个片段let r1 = rvlet rr1 = &mut rv第一个片段的变化,并允许使用if let Some(v)模式,其中v仍然是对向量的可变引用的引用.这也要求v_option声明为可变.

  • 在书中:https://doc.rust-lang.org/book/patterns.html#ref-and-ref-mut(非常非常简洁). (2认同)