Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

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.

like image 859
colinfang Avatar asked Apr 28 '17 13:04

colinfang


People also ask

What will happen if we put a key object in a HashMap which is already there?

If the key given already exist in HashMap, the value is replaced with the new value. hash code of the null key is 0.

How to add to a key in a map?

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.


1 Answers

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 selfby 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
}
like image 55
Shepmaster Avatar answered Sep 28 '22 22:09

Shepmaster