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