2

I want to create a function which gets the value associated to a key in a hashtable, and, if such value does not exist, inserts an arbitrary value (let us say 0).

use std::collections::HashMap;

fn get_or_insert(table: &mut HashMap<i32, i32>, key: i32) -> i32 {
    match table.get(&key) {
        None => table.insert(key, 0).unwrap(),
        Some(v) => *v,
    }
}

This code does not compile:

error[E0502]: cannot borrow `*table` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:17
  |
4 |     match table.get(&key) {
  |           ----- immutable borrow occurs here
5 |         None => table.insert(key, 0).unwrap(),
  |                 ^^^^^ mutable borrow occurs here
6 |         Some(v) => *v,
7 |     }
  |     - immutable borrow ends here

Indeed, table is mutably borrowed in the method insert whereas it is immutably borrowed in the method get.

I can see no way to separate the mutable and immutable parts in this function.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user19018
  • 2,199
  • 5
  • 16
  • 19

1 Answers1

2

This is a great time to use the entry method:

use std::collections::HashMap;

fn get_or_insert(table: &mut HashMap<i32, i32>, key: i32) -> i32 {
    *table.entry(key).or_insert(0)
}

fn main() {}

But you are correct, otherwise you'd have to split the call:

fn get_or_insert(table: &mut HashMap<i32, i32>, key: i32) -> i32 {
    match table.get(&key) {
        None => {}
        Some(v) => return *v,
    }

    table.insert(key, 0).unwrap()
}

However, you have to calculate the hash a second time, which is part of the reason that entry was created.

As a side note, insert returns the previous value for the key. If I'm reading it correctly, your unwrap call will always fail.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366