我尝试实现自己的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() {}
错误:
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
我知道有一些标准方法可以实现这个功能,但是我希望这个方法尽可能轻松 - 它会非常频繁地调用,几乎所有的时候都会存在这些值。
据我了解,当我们致电self.types.get
时,我们将其借用到匹配语句的范围内,因此我们无法在此处调用self.types.insert
。我试图将无分支中的方法移出匹配语句,但它也失败了。
我找到的唯一可行解决方案需要两次调用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()
}
我如何处理这种情况?
答案 0 :(得分: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
})
}
但是,在你的情况下,里面有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
})
}
答案 1 :(得分:1)
请注意,在第一种情况下,当密钥存在于地图中时,您正在执行一次查找,而当三不存在时,三次。在任何一种情况下,您的上次尝试都会进行两次查找。这是后者的一些美化版本:
pub fn get_type<'a>(&'a mut self, k: i32) -> &'a i32 {
let contains = self.types.contains_key(&k);
if !contains {
self.types.insert(k, self.next);
self.next += 1;
}
self.types.get(&k).unwrap()
}
由于借用限制,我不认为在没有地图实施的某些支持的情况下可以避免第二次查找。
在任何情况下,使用solution by Chris Morgan都优于上述(例如,它可能更有效,实际上需要更少的查找),所以我建议坚持使用它。