A problem about mutable borrow twice

I have the following codes(playground):

use std::collections::HashMap;

fn get_immut(immut_dict: &HashMap<i32, Vec<i32>>, i: i32) -> &Vec<i32> {

fn get_mut(mut_dict: &mut HashMap<i32, Vec<i32>>, i: i32) -> &Vec<i32> {
    mut_dict.insert(i + 1, vec![i + 1]);
    get_immut(mut_dict, i)

fn get_immut_twice(mut_dict: &mut HashMap<i32, Vec<i32>>) -> Vec<i32> {
    let data1 = get_mut(mut_dict, 1);
    let data2 = get_mut(mut_dict, 2);
    data1.iter().zip(data2.iter()).map(|(x, y)| x + y).collect()

The complie error says that in get_immut_twice I mutable borrow the argument mut_dict twice.

I understand it in this way:
After I get let data1 = get_mut(mut_dict, 1), as long as data1 exits, mut_dict is being mutable borrowed. If I call let data2 = get_mut(mut_dict, 2), the compile think I may change the existed data1 by calling the function get_mut. So the error happened.

But I know that data1 would not be changed, what should I do?

This happens when you coerce a &mut T to &T. The lifetime doesn't change, so it remains uniquely borrowed for the duration of the &T.

In this case you might be able to use get_many_mut . Or the equivalent method in hashbrown if you can't or don't want to use nightly.

1 Like

In general, this sort of thinking is flawed.

This isn't something the compiler can prove statically. It also depends on the internals of hashmap - it would not remain valid if the map had to reallocate, for example.

1 Like

With this particular exact code and API, since the .insert can actually invalidate the previously obtained reference to a HashMap item (e.g. in case the map is full and needs to re-allocate to grow), there’s really a bug here that needs to be addressed. The straightforward approach would be to decouple the mutation from the lookup, and do both mutations first, then both (immutable) lookups.

1 Like