我该如何在向 `HashMap` 插入元素后立即获取键和值的引用?

8
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()


1
这是因为您在调用insert方法并使用Self后,已经失去了您的“空闲”对象。那么先获取key再插入值怎么样?我不太明白您为什么需要它?您已经有了键k - Victor Polevoy
还是不行。原始的 k 已经被 entry 使用了。 - colinfang
1个回答

4
TL;DR: 您现在无法(?)
编译器会告诉你为什么它不起作用。不要犹豫阅读Rust编译器的信息,它们并不可怕,而且已经投入了很多努力!
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,返回插入值的引用:
fn insert(self, value: V) -> &'a mut V

这个错误消息有超过100个其他的问题/答案对,所以我会假设你已经阅读了足够多的内容来理解问题;毕竟我们在Stack Overflow上回答问题就是为了这个!


那么为什么会是这样呢?回答这个问题比较困难。当你向HashMap插入内容时,它会拥有该键和值的所有权。当你使用entry API时,有一个中间步骤-你已经将键的所有权交给了entry。entry还具有对HashMap的可变引用;即值所在位置的“书签”。
当该值缺失并且您插入一个值时,键被转移到entry之外,并进入HashMap。这意味着entry内部对键的引用将无效。这就是为什么不能重新排列这两行代码的原因。
但是,如果深入思考一下,从insert返回的值是在值插入后引用的底层HashMap。我看不出有任何理由阻止添加一个函数,该函数返回对键和值的引用。但是,现在不存在这样的功能。
另请参见如何在将键插入HashMap后保留对其引用的方式?
我很确定在这种情况下,你不需要这个功能。
如果你想为新值调用它,只需在插入之前完成所有操作即可。
hook(&k, &v);
a.insert(k, v);

如果您只想对旧值进行操作,并且在以前不存在值时不执行任何操作,则可以:
Occupied(mut occupied) => {
    let old = occupied.insert(v);
    hook(occupied.key(), &old);
    Some(old)
}

如果您希望在添加之前调用钩子并传递旧值(如果存在)和新值(如果插入),则可以这样做,hook函数不会感知到参数的变化,因为它们只是引用。
Vacant(vacant) => {
    hook(vacant.key(), &v);
    let v = vacant.insert(v);
    None
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接