关于rust:插入“ HashMap”后,如何立即获得对键和值的引用?

How can I get a reference to the key and value immediately after inserting into a `HashMap`?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::collections::HashMap;
use std::collections::hash_map::Entry::*;

fn hook(k: &str, v: &str) {}

fn tt(k: String, v: String) -> Option<String> {
    let mut a: HashMap<String, String> = HashMap::new();
    match a.entry(k) {
        Occupied(mut occupied) => {
            let old = occupied.insert(v);
            //hook(&k, &old);
            Some(old)
        }
        Vacant(vacant) => {
            let v = vacant.insert(v);
            let k = vacant.key(); // Why doesn't it work?
            //hook(&k, v);
            None
        }
    }
}

我想在将密钥插入HashMap后立即调用hook。 看来我必须使用Entry。 但是,我无法在vacant.insert之后立即调用vacant.key()


TL; DR:您不能(现在?)

编译器会告诉您为什么它不起作用。不要犹豫,阅读Rust编译器消息;它们并不可怕,已经付出了很多努力!

1
2
3
4
5
6
7
8
9
error[E0382]: use of moved value: `vacant`
  --> src/main.rs:16:21
   |
15 |             let v = vacant.insert(v);
   |                     ------ value moved here
16 |             let k = vacant.key();
   |                     ^^^^^^ value used here after move
   |
   = note: move occurs because `vacant` has type `std::collections::hash_map::VacantEntry<'_, std::string::String, std::string::String>`, which does not implement the `Copy` trait

这只是正常的Rust代码。 VacantEntry::insert按值消耗self,返回对插入值的引用:

1
fn insert(self, value: V) -> &'a mut V

关于该错误消息的其他问题/答案对有100多个,因此,我假设您已经阅读了足够多的问题/答案,以了解问题所在;这就是为什么我们毕竟要回答有关堆栈溢出的问题!

那么为什么会这样呢?这很难回答。当您插入HashMap时,它将获取键和值的所有权。使用entry API时,有一个中间步骤-您已赋予条目键的所有权。该条目还具有对HashMap的可变引用;值所在的"书签"。

如果缺少该值,然后您插入一个值,则密钥将从条目中移出并进入HashMap。这意味着对条目内部键的引用将无效。这就是为什么您不能重新排列这两行的原因。

但是,考虑得更深一点,在插入值之后,从insert返回的值将引用基础HashMap。我看不到任何阻止添加返回键和值引用的函数的原因。但是,这样的功能现在不存在。

另请参见将密钥插入HashMap后如何保留对其的引用?

我很确定在这种情况下您不需要该功能。

如果要为新值调用它,只需在插入之前完成所有这些操作:

1
2
hook(&k, &v);
a.insert(k, v);

如果您只想对旧值执行此操作,而在以前没有值时不执行任何操作,则可以:

1
2
3
4
5
Occupied(mut occupied) => {
    let old = occupied.insert(v);
    hook(occupied.key(), &old);
    Some(old)
}

如果要使用旧值调用钩子(如果有的话),而用新值插入(似乎不一致),则可以在添加之前调用它,由于参数只是引用,因此hook函数将不明智。 :

1
2
3
4
5
Vacant(vacant) => {
    hook(vacant.key(), &v);
    let v = vacant.insert(v);
    None
}