rusthashmappattern-matchingrust-cargo

Rust idomatic way to decrementing the value from a map and remove the key when value is 0


Noob to Rust and trying to figure out the idomatic way to decrement the value from a hashmap and remove the corresponding key when the value after decrementing reaches 0. I am doing it like this but not sure if its the best way to do it:

use std::collections::HashMap;

fn main() {
    let mut frequency = HashMap::from([(2, 1), (3, 4), (5, 6)]);
    let key = 2;

    if let Some(val) = frequency.get_mut(&key) {
        *val -= 1;
        if *val == 0 {
            frequency.remove(&key);
        }
    }
    println!("{:?}", frequency);
}

The other I thought of is to use match construct on the value as below:

use std::collections::HashMap;

fn main() {
    let mut frequency = HashMap::from([(2, 1), (3, 4), (5, 6)]);
    let key = 2;

    if let Some(val) = frequency.get_mut(&key) {
        match *val {
            1 => {
                frequency.remove(&key);
            }
            _ => *val -= 1,
        }
    }
    println!("{:?}", frequency);
}

Solution

  • I don't think there's an idiomatic way for using if or match. Readability is most important, so this may be your personal choice.

    However, in both cases you should use entry() to avoid doing the lookup twice:

    let mut frequency = HashMap::from([(2, 1), (3, 4), (5, 6)]);
    
    if let Entry::Occupied(mut o) = frequency.entry(2) {
        *o.get_mut() -= 1;
        if *o.get() == 0 {
            o.remove_entry();
        }
    }
    

    Using entry, you can even match the result directly using guards:

    match frequency.entry(2) {
        Entry::Occupied(o) if *o.get() == 1 => {
            o.remove_entry();
        }
        Entry::Occupied(mut o) => *o.get_mut() -= 1,
        Entry::Vacant(_) => (),
    };