Get a reference to a newly added item in a HashMap

When adding an item to a HashMap it will be moved. I often need to access this item after this insertion which forces me to do a subsequent get which feels cumbersome and causes another unnecessary key lookup.

Here’s my code and some failed attempts to get around the second lookup using Entry: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7cc91b8606aad0e274d72504751f6dbb

Is there a more idiomatic way to achieve that?

1 Like

How about storing your items somewhere else™ and just put &item as a value into the HashMap?

match map.entry(key) {
  Occupied(entry) => {
    entry.insert(item);
    entry.into_mut()
  }
  Vacant(entry) => entry.insert(item)
}
4 Likes

Thank you!

I’ve been pretty close :wink:

#![allow(unused_imports)]
use std::collections::HashMap;
use std::collections::hash_map::*;

#[derive(Debug)]
struct Item(());

fn main () {
    {
        let mut map = HashMap::new();
        let item = Item(());
        let key = ();
    
        map.insert(key, item); // item moves into map
        // re-fetch item from map:
        let item_ref = map.get(&key).expect("Cannot find previously inserted entity");
        println!("{:?}", item_ref);
    }
    
    {
        let mut map = HashMap::new();
        let item = Item(());
        let key = ();
    
        // insert and return item
        let item_ref = match map.entry(key) {
            Entry::Occupied(mut entry) => {
                entry.insert(item);
                entry.into_mut()
            }
            Entry::Vacant(entry) => entry.insert(item)
        };  
        
        println!("{:?}", item_ref);
    }

}

An integrated method would be nice, though.

There’s also Entry::or_insert, which is essentially the same as the match you’ve written, just without replacing the value of existing occupied entries.

2 Likes

Thanks, but I needed to overwrite an existing value.

Then use and_modify along with or_insert?

That was my first attempt but I could figure out how to replace the value with and_modify.

Is and_modify(|x| *x = <new value>) not sufficient?

1 Like

Huh … must have misread the error message while trying this :slight_smile:

However:

let item_ref = map.entry(key).and_modify(|existing| *existing = item).or_insert(item);

would be a double-move for item or did I overlook something else?

1 Like

It will be a double move. The best way is to match on the entry directly and use into_mut like @Hyeonu showed earlier