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

问题描述 投票:8回答:1

我有以下代码将一些值插入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);
}

不将第一次调用的结果存储到get_mut()不会导致映射在其他范围内被可变地借用?我怎么能从文档中了解到这一点?我错过了别的什么吗?

rust
1个回答
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的引用。方法完成后,借用地图就结束了。

© www.soinside.com 2019 - 2024. All rights reserved.