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`.