为什么 HashMap::get_mut() 获得其余范围的地图所有权? [英] Why does HashMap::get_mut() take ownership of the map for the rest of the scope?

查看:44
本文介绍了为什么 HashMap::get_mut() 获得其余范围的地图所有权?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

使用 std::collections::HashMap;fn东西(){让 mut map = HashMap::new();map.insert(5, "东西");地图.插入(4,世界");map.insert(1, "你好");让 mut thing = map.remove(&5);让 mut world = map.get_mut(&4);让 mut hello = map.get_mut(&1);}

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

error[E0499]: 不能一次多次借用 `map` 作为可变的-->src/main.rs:10:21|9 |让 mut world = map.get_mut(&4);|--- 第一个可变借用发生在这里10 |让 mut hello = map.get_mut(&1);|^^^ 第二个可变借用发生在这里11 |}|- 第一次借阅到此结束

在仔细阅读 remove()get_mut() 方法(幸运的是它们彼此非常接近!)从方法签名中没有什么让我脱颖而出的原因 remove() 方法不会可变地借用当前范围的其余部分的映射,而 get_mut() 方法可以.

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

使用 std::collections::HashMap;fn东西(){让 mut map = HashMap::new();map.insert(5, "东西");地图.插入(4,世界");map.insert(1, "你好");让 mut thing = map.remove(&5);map.get_mut(&4);让 mut hello = map.get_mut(&1);}

不存储第一次调用 get_mut() 的结果不会导致映射被可变地借用到作用域的其余部分?我怎么能通过查看文档知道这一点?我还缺什么吗?

解决方案

此错误是 非词法生命周期.启用这些后,原始代码将按原样运行:

使用 std::collections::HashMap;fn东西(){让 mut map = HashMap::new();map.insert(5, "东西");地图.插入(4,世界");map.insert(1, "你好");让 mut thing = map.remove(&5);让 mut world = map.get_mut(&4);让 mut hello = map.get_mut(&1);}fn main() {}

这是因为编译器更聪明,可以看到当您到达 map.get_mut(&1) 时,您不再使用 world,所以它不再需要有一个有效的参考.

您可以通过添加显式作用域在先前版本的 Rust 中获得等效代码:

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

<小时><块引用>

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

它绝对不会那样做.所有权 在 Rust 代码中是一个精确的术语.请注意,错误消息特别指出

<块引用>

之前的map借用发生在这里

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

您真正的问题是为什么要在其余范围内借用它".我们来看看签名:

fn get_mut(&mut self, k: &Q) ->选项<&mut V>在哪里K:借用Q,问:哈希 + 等式,

换句话说,这可以理解为

<块引用>

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

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

我们来看看remove:

fn remove(&mut self, k: &Q) ->选项V在哪里K:借用Q,问:哈希 + 等式,

这将返回一个 owned 值,而不是对 HashMap 的引用.一旦方法完成,地图的借用就结束了.

I have the following code that inserts some values into a HashMap and then gets them back out:

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);
}

Attempting to compile this code gives the following error:

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

After perusing the API docs for both the remove() and get_mut() methods (fortunately they are pretty close to each other!) there is nothing that stands out to me from the method signatures why the remove() method does not mutably borrow the map for the rest of the current scope while the get_mut() method does.

The other piece of data that I have that also mystifies me is that this code compiles:

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);
}

Not storing the result of the first call to get_mut() doesn't cause the map to be mutably borrowed for the rest of the scope? How could I have known this from looking at the documentation? Am I missing something else?

解决方案

This error is a limitation of the implementation of the borrow checker before non-lexical lifetimes. With those enabled, the original code will work as-is:

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() {}

This is because the compiler is smarter and can see that you aren't using world anymore by the time you get to map.get_mut(&1), so it doesn't need to have a valid reference anymore.

You can get equivalent code in previous versions of Rust by adding an explicit scope:

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


Why does HashMap::get_mut() take ownership of the map

It absolutely does not do that. Ownership is a precise term in Rust code. Note that the error message specifically says

previous borrow of map occurs here

A borrow is not ownership. If I borrow your car, I don't own your car.

Your real question is "why does it borrow it for the rest of the scope". Let's look at the signature:

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

In words, this could be read as

Given a mutable reference to a HashMap (&mut self) and something that can be used to find the key (K: Borrow<Q>, Q: Hash + Eq), return a mutable reference to the value if one matches (Option<&mut V>)

However, that returned mutable reference will be changing something in the HashMap, that's why it's a mutable reference at all. You are only allowed to have multiple immutable borrows OR one mutable borrow at a time. This prevents writing code that causes inconsistencies and safety issues.

Let's look at remove:

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

This returns an owned value, not a reference into the HashMap. Once the method is done, the borrow of the map is over.

这篇关于为什么 HashMap::get_mut() 获得其余范围的地图所有权?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆