在尝试实现查找或插入时,HashMap借用了问题

Mik*_*ail 3 rust

我试图实现自己的模拟find_or_insert方法,如下所示:

use std::collections::HashMap;

pub struct SomeManager {
    next: i32,
    types: HashMap<i32, i32>,
}

impl SomeManager {
    pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
        match self.types.get(&k) {
            Some(ref x) => return *x,
            None => {
                self.types.insert(k, self.next);
                self.next += 1;
                return self.types.get(&k).unwrap();
            }
        }
    }
}

fn main() {}
Run Code Online (Sandbox Code Playgroud)

错误:

error[E0502]: cannot borrow `self.types` as mutable because it is also borrowed as immutable
  --> src/main.rs:13:17
   |
10 |         match self.types.get(&k) {
   |               ---------- immutable borrow occurs here
...
13 |                 self.types.insert(k, self.next);
   |                 ^^^^^^^^^^ mutable borrow occurs here
...
18 |     }
   |     - immutable borrow ends here
Run Code Online (Sandbox Code Playgroud)

我知道有一些标准方法可以实现这个功能,但我希望这个方法尽可能轻 - 它会非常频繁地调用,而且几乎所有的时候都会存在这些值.

据我了解,当我们打电话时,我们将self.types.get它借到匹配声明的范围,所以我们不能self.types.insert在这里打电话.我试图将方法从None分支中移出match语句,但它也失败了.

我找到的唯一可行解决方案需要调用get两次:

pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
    let is_none = match self.types.get(&k) {
        Some(ref x) => false,
        None => true,
    };
    if is_none {
        self.types.insert(k, self.next);
        self.next += 1;
    }
    self.types.get(&k).unwrap()
}
Run Code Online (Sandbox Code Playgroud)

我该如何解决这种情况?

Chr*_*gan 8

有一些方法HashMap可以实现这些复杂的案例.最值得注意的是,对于您的情况,HashMap::entry并且Entry::or_insert_with:

pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
    self.types.entry(k).or_insert_with(|| {
        let value = self.next;
        self.next += 1;
        value
    })
}
Run Code Online (Sandbox Code Playgroud)

然而,在你的情况下,借用self内部,这是不可能的.

因此,我们转移了self.next对闭包之外的借用,因此编译器可以将其推断为不相交self.types.只有一次查找解决了问题,应该是这样.

pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
    let next = &mut self.next;

    self.types.entry(k).or_insert_with(|| {
        let value = *next;
        *next += 1;
        value
    })
}
Run Code Online (Sandbox Code Playgroud)