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
.
If the key given already exist in HashMap, the value is replaced with the new value. hash code of the null key is 0.
Use the set() method to add a key/value pair to a Map , e.g. map. set('myKey', 'myValue') . The set() method adds or updates the element with the provided key and value and returns the Map object.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With