不能从 hashmap 中借用可变的值,因为它也被借用为不可变的

Cannot borrow value from a hashmap as mutable because it is also borrowed as immutable

我想同时从一个hashmap中获取两个值,但我无法避免出现以下错误,我将代码简化如下,谁能帮我解决这个错误。

#[warn(unused_variables)]
use hashbrown::HashMap;

fn do_cal(a: &[usize], b: &[usize]) -> usize {
    a.iter().sum::<usize>() + b.iter().sum::<usize>()
}

fn do_check(i: usize, j:usize) -> bool {
    i/2 < j - 10
}

fn do_expensive_cal(i: usize) -> Vec<usize> {
    vec![i,i,i]
}

fn main() {
    let size = 1000000;
    let mut hash: HashMap<usize, Vec<usize>> = HashMap::new();
    for i in 0..size{
        if i > 0 {
            hash.remove(&(i - 1));
        }
        
        if !hash.contains_key(&i){
            hash.insert(i, do_expensive_cal(i));
        }
        let data1 = hash.get(&i).unwrap();
    
        for j in i + 1..size {
            if do_check(i, j) {
                break
            }
            if !hash.contains_key(&j){
                hash.insert(j, do_expensive_cal(j));
            }
            let data2 = hash.get(&j).unwrap();
            let res = do_cal(data1, data2);
            println!("res:{}", res);
    
        }
    }
}

Playground

错误[E0502]:无法借用 hash 作为可变的,因为它也被借用为不可变的

  --> src/main.rs:26:8
   |
19 |         let data1 = hash.get(&i).unwrap();
   |                     ------------ immutable borrow occurs here
...
26 |                 hash.insert(j, vec![1,2,3]);
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
29 |             let res = do_cal(data1, data2);
   |                              ----- immutable borrow later used here

有关此错误的更多信息,请尝试 rustc --explain E0502。 错误:由于先前的错误

,无法编译 playground

考虑一下:借用检查器不知道 hash.insert(j, …) 会单独保留您使用 hash.insert(i, …) 插入的数据。对于借用检查器,hash.insert(…) 可以对 hash 中的任何元素执行任何操作,包括重写或删除它。所以你不能在 hash.insert(j, …).

上保留引用 data1

如何克服?最简单的可能就是移动 let data1 = hash.get(…) 这样它就不用活那么久了:

let data1 = hash.get(&i).unwrap();
let data2 = hash.get(&j).unwrap();
let res = do_cal(data1, data2);

这当然会在每个循环迭代中查找 data1(而且它必须,因为 hash.insert(j, …) 可能已经调整大小并因此重新分配 hashmap 的内容,给 data1 一个新的hashmap 中的存储位置)。为了完整起见,有一些方法可以解决这个问题,但我不建议您这样做:

  • 克隆:let data1 = hash.get(&i).unwrap().clone()(如果你的 vecs 很短,这实际上可能是合理的......)
  • 作为一种降低克隆成本的方法,您可以改用 HashMap<usize, Rc<Vec<usize>>>(您只需要克隆 Rc,而不是整个 Vec
  • 如果您需要对 do_call 的两个参数进行可变引用,您可以将 RcRefCell 结合使用:Rc<RefCell<Vec<…>>>
  • 如果您需要对其进行更多的过度设计,您可以将 Rc 替换为从 bump 分配器中分配获得的引用,例如bumpalo.

由于散列 table 的键是整数 0..100,您可以使用 Vec 来执行这些步骤,暂时将 Vec 分成 2 片以允许一侧的突变。如果你需要一个 HashMap 用于以后的计算,你可以从 Vec.

创建一个 HashMap

以下代码可以编译但会出现错误,因为 j - 10 计算下溢:

fn do_cal(a: &[usize], b: &[usize]) -> usize {
    a.iter().sum::<usize>() + b.iter().sum::<usize>()
}

fn do_check(i: usize, j:usize) -> bool {
    i/2 < j - 10
}

fn main() {
    let size = 100;
    let mut v: Vec<Option<Vec<usize>>> = vec![None; size];
    for i in 0..size {
        let (v1, v2) = v.split_at_mut(i + 1);
        if v1[i].is_none() {
            v1[i] = Some(vec![1,2,3]);
        }
        let data1 = v1[i].as_ref().unwrap();

        for (j, item) in (i + 1..).zip(v2.iter_mut()) {
            if do_check(i, j) {
                break
            }
            if item.is_none() {
                *item = Some(vec![1,2,3]);
            }
            let data2 = item.as_ref().unwrap();
            let res = do_cal(data1, data2);
            println!("res:{}", res);

        }
    }
}