如何在 'Option' 中多次访问一个向量?

How to access a vector multiple times within an 'Option'?

如何在 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 {
   |                     ^^^^^^^^^

按值匹配将值移动到模式变量中。移动会使原始值不可用,除了实现 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 语句现在引用移动的变量 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);
}

push 调用的语法与 r1.pushrr1.push 相同,因为 Rust 的 . 运算符将自动解引用任意数量的引用。

至 return 至问题示例,Option 中对 Vec<usize> 的引用类似于上面的 v 引用,并使用 Some(v) pattern 将引用移动到 v pattern 变量中。

要修复它,必须像上面的示例一样更改模式,以指定一个变量,该变量 引用 本身就是引用的值。这是使用 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()方法将选项内容return作为对先前内容的可变引用。这在概念上等同于第一个代码段中从 let r1 = rvlet rr1 = &mut rv 的更改,并且将允许使用 if let Some(v) 模式,其中 v 仍然是对可变的引用对向量的引用。这也需要将 v_option 声明为可变的。