1
votes

I just got confused by the rust compile error about lifetime.

Suppose the code snippet looks like this:

fn process(map: &mut HashMap<String, String>, key: String) {
    match map.get_mut(&key) {
        Some(value) => println!("value: {}", value),
        None => {
            map.insert(key, String::new());
        }
    }
}

And I called it as follows:

fn main() {
    let mut map = HashMap::<String, String>::new();
    let key = String::from("name");
    process(&mut map, key);
}

As far as I know(ignore the NLL feature), map.get_mut returns a Option<&mut String> type in which &mut String is a borrow pointer which points to part of the map, and the pointer lives through the whole match block. Then inside the None branch, map.insert(key, String::new()) creates another &mut HashMap<String, String> pointer automatically, which also points to the map. The two pointers borrow the same map as mutable twice, so it causes:

error[E0499]: cannot borrow `*map` as mutable more than once at a time
 --> test.rs:7:13
  |
4 |     match map.get_mut(&key) {
  |           --- first mutable borrow occurs here
...
7 |             map.insert(key, String::new());
  |             ^^^ second mutable borrow occurs here
8 |         }
9 |     }
  |     - first borrow ends here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.

But my question is :

The first parameter of function fn process is a mutable pointer itself(&mut HashMap<String, String>), which also points to the map. According to the rule above, when the next line calls map.get_mut(&key), the second mutable borrow occurs. Why the compiler doesn't throws a error like this(any risk of memory safe?):

fn process(map: &mut HashMap<String, String>, key: String)
           --- first mutable borrow occurs here
    match map.get_mut(&key)
    ^^^ second mutable borrow occurs here // the return value of type Option<&mut String>

I'm newcomer to rust, any tips would be appreciated.

1

1 Answers

3
votes

In order to perform the call to get_mut, Rust performs an implicit reborrow.

A reborrow is equivalent to borrowing from a mutable reference and then flattening the references. That is, given a mutable reference of type &'a T, borrowing from it yields a &'b &'a T (note that there are two distinct lifetimes; 'b is shorter than 'a), and flattening the references yields &'b T.

Rust understands that the borrow with lifetime 'b was derived from the borrow with lifetime 'a. Thus, borrow 'a will be frozen for as long as borrow 'b is alive.

Before NLL, the borrow obtained for the call to get_mut would live for the whole match block because get_mut returns a value that keeps the borrow active. map.insert(...) also tries to reborrow from map, but since the first reborrow from map is still active, that's an error.