Rust 尝试删除并插入到HashMap时出现错误的可变借用(E0502)

Rust 尝试删除并插入到HashMap时出现错误的可变借用(E0502),rust,hashmap,borrow-checker,ownership,Rust,Hashmap,Borrow Checker,Ownership,我是一个生锈的初学者,并尝试使用HashMap。我要删除一个元素并用修改后的值插入它: let mut r = HashMap::new(); let mut i = 2; ... if r.contains_key(&i) { let v = r.get(&i).unwrap(); r.remove(&i); r.insert(i, v+1); } 现在,借用检查器抱怨在if块的三行中,r是借用的不可变的,然后是可变的,然后又是不可变的。 我不

我是一个生锈的初学者,并尝试使用HashMap。我要删除一个元素并用修改后的值插入它:

let mut r = HashMap::new();
let mut i = 2;
...
if r.contains_key(&i) {
    let v = r.get(&i).unwrap();
    r.remove(&i);
    r.insert(i, v+1);
}
现在,借用检查器抱怨在if块的三行中,r是借用的不可变的,然后是可变的,然后又是不可变的。 我不明白发生了什么……我想,由于get、remove和insert方法都有r作为隐式参数,所以在这三个调用中借用了r。但为什么移除调用中的借用是可变的呢

但为什么移除调用中的借用是可变的呢

问题在于跨越:Rust允许任意数量的不可变借用或单个可变借用,它们不能重叠

这里的问题是,v是对映射内容的引用,这意味着v的存在需要借用映射,直到v停止使用。因此,它与remove和insert调用重叠,并禁止它们

现在有多种方法可以解决这个问题。由于在这种特定情况下,您使用的是u64,即复制,因此您只需取消引用,它就会复制您从映射中获得的值,从而无需进行借用:

if r.contains_key(&i) {
    let v = *r.get(&i).unwrap();
    r.remove(&i);
    r.insert(i, v+1);
}
不过,这在灵活性上是有限的,因为它只适用于类型[0]

在这种特定情况下,这可能没什么大不了的,因为拷贝很便宜,但为了安全、清晰,以及因为最终需要它们来处理不那么琐碎的类型,使用Rust提供的高级API还是更有意义的

最简单的就是使用:where返回一个选项,返回一个选项,这意味着您可以。。。就地更新值,您不需要将其取出,也不需要将其重新插入,也不需要单独查找,但实际上您已经不需要了:

if let Some(v) = r.get_mut(&i) {
    *v += 1;
}
对于您的用例来说已经足够了

第二种选择是,这将永远毁掉其他HashMapAPI。我不是在开玩笑,其他任何语言都会让人感到可笑的沮丧,你可能希望避免点击该链接,尽管你最终需要了解它,因为它解决了真正的借贷和效率问题

它在这里并没有真正展示它的内容,因为您的用例很简单,而且比工作做得更多,但无论如何,您可以将增量编写为:

r.entry(i).and_modify(|v| *v+=1);
顺便说一句,在大多数语言中,当然在Rust中,当您在hashmap中插入一个项时,如果存在一个值,则旧值会被逐出。因此,remove调用已经是多余的,完全没有必要了

模式匹配一个选项,比如HashMap::get返回的选项,通常比痛苦地按程序执行所有低级位更安全、更干净、更快

因此,即使不使用高级API,原始代码也可以简化为:

if let Some(&v) = r.get(&i) {
    r.insert(i, v+1);
}
我仍然建议使用get_mut版本,因为它更简单,避免了双重查找,并且适用于非复制类型,但不适用于YMMV

与大多数语言不同的是,Rust的HashMap::insert返回旧的值f any,这里不需要担心,但在某些情况下可能很有用

[0]以及通过显式调用.clone,这可能会或可能不会转化为显著的性能影响,具体取决于您要克隆的类型

但为什么移除调用中的借用是可变的呢

问题在于跨越:Rust允许任意数量的不可变借用或单个可变借用,它们不能重叠

这里的问题是,v是对映射内容的引用,这意味着v的存在需要借用映射,直到v停止使用。因此,它与remove和insert调用重叠,并禁止它们

现在有多种方法可以解决这个问题。由于在这种特定情况下,您使用的是u64,即复制,因此您只需取消引用,它就会复制您从映射中获得的值,从而无需进行借用:

if r.contains_key(&i) {
    let v = *r.get(&i).unwrap();
    r.remove(&i);
    r.insert(i, v+1);
}
不过,这在灵活性上是有限的,因为它只适用于类型[0]

在这种特定情况下,这可能没什么大不了的,因为拷贝很便宜,但为了安全、清晰,以及因为最终需要它们来处理不那么琐碎的类型,使用Rust提供的高级API还是更有意义的

最简单的就是使用:where返回一个选项,返回一个选项,这意味着您可以。。。就地更新值,您不需要将其取出,也不需要将其重新插入,也不需要单独查找,但实际上您已经不需要了:

if let Some(v) = r.get_mut(&i) {
    *v += 1;
}
对于您的用例来说已经足够了

第二种选择是,这将永远毁掉其他HashMapAPI。我不是在开玩笑,其他任何语言都会让人感到可笑的沮丧,你可能希望避免点击该链接,尽管你最终需要了解它,因为它解决了真正的借贷和效率问题

它并没有真正在这里展示它的内容,因为您的用例很简单,而且可以得到更多 但无论如何,您可以将增量写为:

r.entry(i).and_modify(|v| *v+=1);
顺便说一句,在大多数语言中,当然在Rust中,当您在hashmap中插入一个项时,如果存在一个值,则旧值会被逐出。因此,remove调用已经是多余的,完全没有必要了

模式匹配一个选项,比如HashMap::get返回的选项,通常比痛苦地按程序执行所有低级位更安全、更干净、更快

因此,即使不使用高级API,原始代码也可以简化为:

if let Some(&v) = r.get(&i) {
    r.insert(i, v+1);
}
我仍然建议使用get_mut版本,因为它更简单,避免了双重查找,并且适用于非复制类型,但不适用于YMMV

与大多数语言不同的是,Rust的HashMap::insert返回旧的值f any,这里不需要担心,但在某些情况下可能很有用


[0]以及通过显式调用.clone,这可能会或可能不会转化为显著的性能影响,具体取决于您要克隆的类型。

问题在于,在获取v时,您保留了一个不可变的引用。由于它是一个u64,因此只需隐式克隆即可,因此不再涉及引用:

让v=r.get&i.unwrap.clone;
问题是,在获取v时,您保留了一个不可变的引用。由于它是一个u64,因此只需隐式克隆即可,因此不再涉及引用:

让v=r.get&i.unwrap.clone;