为什么HashMap :: get_mut()取得了该范围其余部分的地图所有权?

时间:2015-09-24 12:37:26

标签: rust

我有以下代码将一些值插入HashMap,然后将它们取回:

use std::collections::HashMap;

fn things() {
    let mut map = HashMap::new();
    map.insert(5, "thing");
    map.insert(4, "world");
    map.insert(1, "hello");
    let mut thing = map.remove(&5);
    let mut world = map.get_mut(&4);
    let mut hello = map.get_mut(&1);
}

尝试编译此代码会出现以下错误:

error[E0499]: cannot borrow `map` as mutable more than once at a time
  --> src/main.rs:10:21
   |
9  |     let mut world = map.get_mut(&4);
   |                     --- first mutable borrow occurs here
10 |     let mut hello = map.get_mut(&1);
   |                     ^^^ second mutable borrow occurs here
11 | }
   | - first borrow ends here

在仔细阅读remove()get_mut()方法的API文档之后(幸运的是它们彼此非常接近!)从方法签名中我没有什么能够突出{{{当remove()方法执行时,方法不会为当前范围的其余部分可变地借用地图。

我所拥有的另一条数据也让我感到困惑的是这段代码编译:

get_mut()

不将第一次调用的结果存储到use std::collections::HashMap; fn things() { let mut map = HashMap::new(); map.insert(5, "thing"); map.insert(4, "world"); map.insert(1, "hello"); let mut thing = map.remove(&5); map.get_mut(&4); let mut hello = map.get_mut(&1); } 并不会导致地图被其他范围内的可变借用?我怎么能从文档中了解到这一点?我错过了别的什么吗?

1 个答案:

答案 0 :(得分:11)

此错误限制了non-lexical lifetimes之前借用检查程序的实施。启用后,原始代码将按原样运行:

use std::collections::HashMap;

fn things() {
    let mut map = HashMap::new();
    map.insert(5, "thing");
    map.insert(4, "world");
    map.insert(1, "hello");
    let mut thing = map.remove(&5);
    let mut world = map.get_mut(&4);
    let mut hello = map.get_mut(&1);
}

fn main() {}

这是因为编译器更智能,并且在您到达world时可以看到您不再使用map.get_mut(&1),因此它不再需要有效参考。

通过添加显式范围,您可以在以前版本的Rust中获得等效代码:

let mut thing = map.remove(&5);
{
    let mut world = map.get_mut(&4);
}
let mut hello = map.get_mut(&1);
  

为什么HashMap::get_mut()取得地图的所有权

绝对这样做。 所有权是Rust代码中的精确术语。请注意,错误消息具体说明

  

此前借用map发生在这里

借用不是所有权。如果我借你的车,我没有你的车。

你真正的问题是“为什么它会在剩下的范围内借用它”。我们来看看签名:

fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> 
where
    K: Borrow<Q>,
    Q: Hash + Eq,

用语言来说,这可以理解为

  

给定对HashMap&mut self)的可变引用以及可用于查找键(K: Borrow<Q>, Q: Hash + Eq)的内容,如果匹配,则返回对该值的可变引用( Option<&mut V>

然而,返回的可变引用将改变HashMap中的某些,这就是为什么它根本就是一个可变引用。您只能拥有多个不可变借款或一次可变借款。这可以防止编写导致不一致和安全问题的代码。

让我们看一下remove

fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> 
where
    K: Borrow<Q>, 
    Q: Hash + Eq,

这将返回拥有的值,而不是HashMap的引用。方法完成后,借用地图就结束了。