我有以下代码将一些值插入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);
}
并不会导致地图被其他范围内的可变借用?我怎么能从文档中了解到这一点?我错过了别的什么吗?
答案 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
的引用。方法完成后,借用地图就结束了。