【问题标题】:Mutate a Vec element with a capturing pattern match使用捕获模式匹配来改变 Vec 元素
【发布时间】:2021-04-17 05:37:58
【问题描述】:

我可以更改 vec 的最后一个元素:

#[derive(Debug)]
enum Type {
    A,
    B,
    C,
}

fn main() {
    let mut v = vec![Type::A, Type::B, Type::B];
    
    match v.last_mut(){
        None => v.push(Type::A),
        Some(last) => *last = Type::C,
    }
    
    println!("{:?}", v)
}

==> 打印出[A, B, C]

但如果我的枚举常量有数据,我似乎无法捕获它们...... 例如:

#[derive(Debug)]
enum Type {
    A(i32),
    B(i32),
    C(i32),
}

fn main() {
    let mut v = vec![Type::A(0), Type::B(1), Type::B(2)];
    
    match v.last_mut(){
        None => v.push(Type::A(0)),
        Some(last @ Type::B(_)) => *last = Type::C(42),
        Some(Type::A(_)) | Some(Type::C(_)) => {}
    }
    
    println!("{:?}", v);
}

==> 打印 [A(0), B(1), C(42)]

上面的工作只是因为我在Type::B(_) 里面放了一个_。 如果我尝试使用此行捕获它以在Type::C() 中使用:

        Some(last @ Type::B(p)) => *last = Type::C(*p),

我收到三个奇怪的错误:

error: borrow of moved value
  --> src/main.rs:13:14
   |
13 |         Some(last @ Type::B(p)) => *last = Type::C(*p),
   |              ----^^^^^^^^^^^-^
   |              |              |
   |              |              value borrowed here after move
   |              value moved into `last` here
   |              move occurs because `last` has type `&mut Type` which does not implement the `Copy` trait

error[E0658]: pattern bindings after an `@` are unstable
  --> src/main.rs:13:29
   |
13 |         Some(last @ Type::B(p)) => *last = Type::C(*p),
   |                             ^
   |
   = note: see issue #65490 <https://github.com/rust-lang/rust/issues/65490> for more information

error[E0382]: borrow of moved value
  --> src/main.rs:13:29
   |
13 |         Some(last @ Type::B(p)) => *last = Type::C(*p),
   |              ---------------^-
   |              |              |
   |              |              value borrowed here after move
   |              value moved here
   |
   = note: move occurs because value has type `&mut Type`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving the value
   |
13 |         Some(ref last @ Type::B(p)) => *last = Type::C(*p),
   |              ^^^

error: aborting due to 3 previous errors

我怎样才能完成这项工作,即捕获 B 的当前值(a &amp;mut i32),并将其传递给 C?

【问题讨论】:

    标签: design-patterns rust match capture mutable


    【解决方案1】:

    可变地捕获last 并且不可变地捕获其中的值意味着您同时持有mut 引用和对相同数据的共享引用。 Rust 的所有权模型禁止这种别名,因此借用检查器拒绝它。

    我怎样才能完成这项工作,即捕获 B 的当前值(a &amp;mut i32),并将其传递给 C?

    您可以捕获最后一个值并在单独的内部匹配中对其进行检查。这避免了对别名引用的初始请求,这对 Rust 来说是个问题。相反,它从外部 last 引用创建内部 p 引用,只要在内部引用的生命周期内不使用外部引用,就允许这样做。换句话说,这编译:

    match v.last_mut() {
        None => v.push(Type::A(0)),
        Some(last) => {
            match last {
                Type::B(p) => *last = Type::C(*p),
                Type::A(_) | Type::C(_) => (),
            }
        }
    }
    

    Playground

    【讨论】:

    • 太好了,谢谢!只有我不确定它为什么会起作用......为什么分成两个匹配 Rust 的所有权模型不再检测到别名? last 不是可变捕获的,它里面的值不是像以前一样不变吗?
    • @rsalmei 这是个好问题。我的心理模型是,在嵌套匹配版本中,内部匹配基于last 中的外部mut 引用创建共享引用。只要您在内部引用处于活动状态时不使用外部引用,这是允许的(并且必须允许,否则您将无法对 &amp;mut 引用执行任何有用的操作,例如访问数据在里面!) - 事实上,就我而言,我们放弃了它。在您的原始代码中,匹配模式请求对相同数据的 mut 和非 mut 引用,这是别名。
    • @rsalmei 我相信您问题中的代码等同于尝试执行 this (无法编译),而我的答案中的代码等同于 this reformulation (可编译) .在这个更简单的情况下,我们也可以将let r2: &amp;Foo = &amp;foo 更改为let r2: &amp;Foo = r1,但你不能告诉match 这样做,你必须基于r1 创建一个新的内部匹配。
    • 确实,NLL 后的 rustc 足够聪明,可以在外部引用之后不使用内部引用时允许“别名”:例如 this compiles as well。但是,如果您在分配给*last 之后尝试触摸*p,它会no longer compile
    • 哇,你成功了!非常感谢!对共享引用的“降级”,NLL 示例确实解释了这一点!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 2021-11-26
    • 2023-03-15
    • 2013-06-10
    • 2014-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多