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

rust

Solution

TL;DR: You cannot (right now?)

The compiler tells you why it doesn't work. Don't hesitate to read Rust compiler messages; they aren't scary and a lot of effort has gone into them!

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

This is just normal Rust code. `VacantEntry::insert` consumes `self`by value, returning a reference to the inserted value:

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

There are over 100 other question/answer pairs that ask about this error message, so I'll assume you've read enough of them to understand the problem; that's why we answer questions on Stack Overflow after all!

So why is it this way? That's tougher to answer. When you insert into a `HashMap`, it takes ownership of the key and the value. When you use the `entry` API, there's an intermediate step - you've given ownership of the key to the entry. The entry also has a mutable reference to the `HashMap`; a "bookmark" of where the value is.

When the value is missing and then you insert a value, the key is transferred out of the entry and into the `HashMap`. This means that the reference to the key inside the entry would be invalidated. That's why you can't reorder the two lines.

However, thinking about it a bit deeper, the value returned from `insert` refers to the underlying `HashMap`, after the value has been inserted. I can't see any reason preventing a function from being added that returns a reference to the key and the value. However, such a function doesn't exist now.

See also How can I keep a reference to a key after it has been inserted into a HashMap?

I'm pretty sure in this case you don't need the functionality though.

If you want to call it for the new value, just do all of this before the insertion:

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

If you want to do it only for the old value, doing nothing when there wasn't previously a value, you can:

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

If you want to call the hook with the old value if there was one and the new value if inserting (which seems inconsistent), you can call it before adding, the `hook` function will be none the wiser as the arguments are just references:

Vacant(vacant) => {
    hook(vacant.key(), &v);
    let v = vacant.insert(v);
    None
}

Problem

``` 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 } } } ``` I would like to call `hook` immediately after a key is inserted into the `HashMap`. It seems I have to use `Entry`. However, I am unable to call `vacant.key()` right after `vacant.insert`.

Original source

Related problems