Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default mutable value from HashMap

Tags:

rust

lifetime

Suppose I have a HashMap and I want to get a mutable reference to an entry, or if that entry does not exist I want a mutable reference to a new object, how can I do it? I've tried using unwrap_or(), something like this:

fn foo() {
    let mut map: HashMap<&str, Vec<&str>> = HashMap::new();

    let mut ref = map.get_mut("whatever").unwrap_or( &mut Vec::<&str>::new() );

    // Modify ref.
}

But that doesn't work because the lifetime of the Vec isn't long enough. Is there any way to tell Rust that I want the returned Vec to have the same lifetime as foo()? I mean there is this obvious solution but I feel like there should be a better way:

fn foo() {
    let mut map: HashMap<&str, Vec<&str>> = HashMap::new();

    let mut dummy: Vec<&str> = Vec::new();
    let mut ref = map.get_mut("whatever").unwrap_or( &dummy );

    // Modify ref.
}
like image 844
Timmmm Avatar asked Jun 30 '15 14:06

Timmmm


2 Answers

As mentioned by Shepmaster, here is an example of using the entry pattern. It seems verbose at first, but this avoids allocating an array you might not use unless you need it. I'm sure you could make a generic function around this to cut down on the chatter :)

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

fn foo() {
    let mut map = HashMap::<&str, Vec<&str>>::new();
    let mut result = match map.entry("whatever") {
       Vacant(entry) => entry.insert(Vec::new()),
       Occupied(entry) => entry.into_mut(),
    };

    // Do the work
    result.push("One thing");
    result.push("Then another");
}

This can also be shortened to or_insert as I just discovered!

use std::collections::HashMap;

fn foo() {
    let mut map = HashMap::<&str, Vec<&str>>::new();
    let mut result = map.entry("whatever").or_insert(Vec::new());

    // Do the work
    result.push("One thing");
    result.push("Then another");
}
like image 193
jocull Avatar answered Oct 16 '22 11:10

jocull


If you want to add your dummy into the map, then this is a duplicate of How to properly use HashMap::entry? or Want to add to HashMap using pattern match, get borrow mutable more than once at a time (or any question about the entry API).

If you don't want to add it, then your code is fine, you just need to follow the compiler error messages to fix it. You are trying to use a keyword as an identifier (ref), and you need to get a mutable reference to dummy (& mut dummy):

use std::collections::HashMap;

fn foo() {
    let mut map: HashMap<&str, Vec<&str>> = HashMap::new();

    let mut dummy: Vec<&str> = Vec::new();
    let f = map.get_mut("whatever").unwrap_or( &mut dummy );
}

fn main() {}
like image 30
Shepmaster Avatar answered Oct 16 '22 10:10

Shepmaster