How to insert into a hash map a key value pair if and only if the key is not already in the map?

I have this method:

    pub fn AddCab(&mut self,name: String,color: String) -> &Cab {               
        match self.cabs.get(&name) {                                            
            None => {                                                           
                self.cabs.insert(name,Cab::new(name.clone(),color));            
                &self.cabs[&name]                                               
            },                                                                  
            Some(c) => c,                                                       
        }                                                                       
    }                                                                           

And I am getting this error:

error[E0502]: cannot borrow `self.cabs` as mutable because it is also borrowed a
s immutable
   --> src/lib.rs:699:17
    |
696 |       pub fn AddCab(&mut self,name: String,color: String) -> &Cab {
    |                     - let's call the lifetime of this reference `'1`
697 |           match self.cabs.get(&name) {
    |           -     --------- immutable borrow occurs here
    |  _________|
    | |
698 | |             None => {
699 | |                 self.cabs.insert(name,Cab::new(name.clone(),color));
    | |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ muta
ble borrow occurs here
700 | |                 &self.cabs[&name]
701 | |             },
702 | |             Some(c) => c,
703 | |         }
    | |_________- returning this value requires that `self.cabs` is borrowed for
 `'1`

The cabs field is declared as: cabs: CabNameMap.

Is there a way around this?

Yes, use the entry function instead of get/insert.

I don't want to modify the entry, only insert if it is not there. I am not sure if the entry method is the right thing.

Would this work for you? This is simply a snippet from the docs for HashMap

// update a key, guarding against the key possibly not being set
let stat = player_stats.entry("attack").or_insert(100);

Otherwise, a code we can run (sort of) in the playground may be useful.

2 Likes

Wouldn't if clause be easier?

pub fn AddCab(&mut self,name: String,color: String) -> &Cab {               
        if self.cabs.contains_key(&name) {
                self.cabs.insert(name,Cab::new(name.clone(),color));            
                
         }
         &self.cabs[&name]                                               
    }        

...although, now I prefer not to write such wrapping code, it just makes it unclear what is happening. I'd put this if into caller code, unless this repeats very often.

1 Like

You use the or_insert method. Entry in std::collections::hash_map - Rust

This should do what you need:

pub fn AddCab(&mut self, name: String, color: String) -> &Cab {
    self.cabs.entry(name).or_insert(Cab::new(name, color))
}
5 Likes

The entry API is made for exactly this, avoiding the borrow issue.

1 Like

What a coincidence, I'm writing examples for AI training, and this is one of the cases.