I have a function f that accepts two references, one mut and one not mut. I have values for f inside a HashMap:
use std::collections::HashMap;
fn f(a: &i32, b: &mut i32) {}
fn main() {
    let mut map = HashMap::new();
    map.insert("1", 1);
    map.insert("2", 2);
    {
        let a: &i32 = map.get("1").unwrap();
        println!("a: {}", a);
        let b: &mut i32 = map.get_mut("2").unwrap();
        println!("b: {}", b);
        *b = 5;
    }
    println!("Results: {:?}", map)
}
This doesn't work because HashMap::get and HashMap::get_mut attempt to  mutably borrow and immutably borrow at the same time:
error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
  --> src/main.rs:15:27
   |
12 |         let a: &i32 = map.get("1").unwrap();
   |                       --- immutable borrow occurs here
...
15 |         let b: &mut i32 = map.get_mut("2").unwrap();
   |                           ^^^ mutable borrow occurs here
...
18 |     }
   |     - immutable borrow ends here
In my real code I'm using a large, complex structure instead of a i32 so it is not a good idea to clone it.
In fact, I'm borrowing two different things mutably/immutably, like:
struct HashMap {
    a: i32,
    b: i32,
}
let mut map = HashMap { a: 1, b: 2 };
let a = &map.a;
let b = &mut map.b;
Is there any way to explain to the compiler that this is actually safe code?
I see how it possible to solve in the concrete case with iter_mut:
{
    let mut a: &i32 = unsafe { mem::uninitialized() };
    let mut b: &mut i32 = unsafe { mem::uninitialized() };
    for (k, mut v) in &mut map {
        match *k {
            "1" => {
                a = v;
            }
            "2" => {
                b = v;
            }
            _ => {}
        }
    }
    f(a, b);
}
But this is slow in comparison with HashMap::get/get_mut
TL;DR: You will need to change the type of HashMap
When using a method, the compiler does not inspect the interior of a method, or perform any runtime simulation: it only bases its ownership/borrow-checking analysis on the signature of the method.
In your case, this means that:
get will borrow the entire HashMap for as long as the reference lives,get_mut will mutably borrow the entire HashMap for as long as the reference lives.And therefore, it is not possible with a HashMap<K, V> to obtain both a &V and &mut V at the same time.
The work-around, therefore, is to avoid the need for a &mut V entirely.
This can be accomplished by using Cell or RefCell:
HashMap into HashMap<K, RefCell<V>>,get in both cases,borrow() to get a reference and borrow_mut() to get a mutable reference.use std::{cell::RefCell, collections::HashMap};
fn main() {
    let mut map = HashMap::new();
    map.insert("1", RefCell::new(1));
    map.insert("2", RefCell::new(2));
    {
        let a = map.get("1").unwrap();
        println!("a: {}", a.borrow());
        let b = map.get("2").unwrap();
        println!("b: {}", b.borrow());
        *b.borrow_mut() = 5;
    }
    println!("Results: {:?}", map);
}
This will add a runtime check each time you call borrow() or borrow_mut(), and will panic if you ever attempt to use them incorrectly (if the two keys are equal, unlike your expectations).
As for using fields: this works because the compiler can reason about borrowing status on a per-field basis.
Something appears to have changed since the question was asked. In Rust 1.38.0 (possibly earlier), the following compiles and works:
use std::collections::HashMap;
fn f(a: &i32, b: &mut i32) {}
fn main() {
    let mut map = HashMap::new();
    map.insert("1", 1);
    map.insert("2", 2);
    let a: &i32 = map.get("1").unwrap();
    println!("a: {}", a);
    let b: &mut i32 = map.get_mut("2").unwrap();
    println!("b: {}", b);
    *b = 5;
    println!("Results: {:?}", map)
}
playground
There is no need for RefCell, nor is there even a need for the inner scope.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With