如何在借用不可变值的匹配中进行变异?

Cor*_*ker 1 ownership rust borrowing

我可以理解 Rust 中的借用/所有权概念,但我不知道如何解决这种情况:

use std::collections::{HashMap, HashSet};

struct Val {
    t: HashMap<u16, u16>,
    l: HashSet<u16>,
}

impl Val {
    fn new() -> Val {
        Val {
            t: HashMap::new(),
            l: HashSet::new(),
        }
    }

    fn set(&mut self, k: u16, v: u16) {
        self.t.insert(k, v);
        self.l.insert(v);
    }

    fn remove(&mut self, v: &u16) -> bool {
        self.l.remove(v)
    }

    fn do_work(&mut self, v: u16) -> bool {
        match self.t.get(&v) {
            None => false,
            Some(r) => self.remove(r),
        }
    }
}

fn main() {
    let mut v = Val::new();

    v.set(123, 100);
    v.set(100, 1234);

    println!("Size before: {}", v.l.len());
    println!("Work: {}", v.do_work(123));
    println!("Size after: {}", v.l.len());
}
Run Code Online (Sandbox Code Playgroud)

操场

编译器有错误:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:28:24
   |
26 |         match self.t.get(&v) {
   |               ------ immutable borrow occurs here
27 |             None => false,
28 |             Some(r) => self.remove(r),
   |                        ^^^^^------^^^
   |                        |    |
   |                        |    immutable borrow later used by call
   |                        mutable borrow occurs here
Run Code Online (Sandbox Code Playgroud)

我不明白为什么我get之前执行(读取值)时无法在火柴臂中变异;self.t.get当变异开始时就结束了remove

Option<&u16>这是由于返回的result () 的范围造成的吗get?确实,结果的生命周期在匹配表达式内有一个范围,但这种设计模式使用得非常频繁(在匹配表达式中进行变异)。

我该如何解决该错误?

rod*_*igo 5

函数的声明HashMap::<K,V>::get()有点简单:

pub fn get<'s>(&'s self, k: &K) -> Option<&'s V>
Run Code Online (Sandbox Code Playgroud)

这意味着它返回对所包含值的可选引用,而不是值本身。由于返回的引用指向映射内的值,因此它实际上借用了映射,也就是说,当该引用存在时,您无法更改映射。这个限制是为了保护你,如果你在引用仍然存在的情况下删除这个值会发生什么?

所以当你写:

match self.t.get(&v) {
    None => false,
    //r: &u16
    Some(r) => self.remove(r)
}
Run Code Online (Sandbox Code Playgroud)

捕获r的类型是&u16,其生命周期是self.t,也就是说,它正在借用它。self因此,您无法获得调用remove所需的可变引用。

解决您的问题的最简单的解决方案是clone()解决每个生命周期问题模式。由于您的值的类型为u16,即Copy,它实际上很简单:

match self.t.get(&v) {
    None => false,
    //r: u16
    Some(&r) => self.remove(&r)
}
Run Code Online (Sandbox Code Playgroud)

Nowr实际上是类型,u16所以它不借用任何东西,你可以self随意变异。

如果您的键/值类型不是,Copy您可以尝试使用clone它们,如果您愿意为此付费的话。如果没有,还有另一个选择,因为您的remove()函数不会修改HashMap但不相关的HashSet. 如果你注意不要重新借用,你仍然可以改变该集合self

    fn remove2(v: &u16, l: &mut HashSet<u16>) -> bool {
        l.remove(v)
    }
    fn do_work(&mut self, v: u16) -> bool {
        match self.t.get(&v) {
            None => false,
            //selt.t is borrowed, now we mut-borrow self.l, no problem
            Some(r) => Self::remove2(r, &mut self.l)
        }
    }
Run Code Online (Sandbox Code Playgroud)