Search code examples
rustborrow-checker

Rust Errors: Cannot borrow as mutable more than once at a time, cannot borrow as immutable because it is also borrowed as mutable


I have the following example code:

let mut errors: Vec<String> = Vec::new();

let msg1 = "message 1".to_string();
let msg2 = "message 2".to_string();
let msg3 = "message 3".to_string();

let mut push_auth_requirements = || {
    errors.push(msg1.clone());
    errors.push(msg2.clone());
};

let mut push_auth_error = || {
    errors.push(msg3.clone());
    push_auth_requirements();
};

// do some stuff

if errors.is_empty() {
    // do more stuff
    if "bob" == "dsadasd" {
        push_auth_requirements();
    } else {
        push_auth_error();
    }
}

println!("Errors: {:?}",errors);

This gives two errors:

error[E0499]: cannot borrow `errors` as mutable more than once at a time
  --> src/main.rs:89:31
   |
84 |     let mut push_auth_requirements = || {
   |                                      -- first mutable borrow occurs here
85 |         errors.push(msg1.clone());
   |         ------ first borrow occurs due to use of `errors` in closure
...
89 |     let mut push_auth_error = || {
   |                               ^^ second mutable borrow occurs here
90 |         errors.push(msg3.clone());
   |         ------ second borrow occurs due to use of `errors` in closure
91 |         push_auth_requirements();
   |         ---------------------- first borrow later captured here by closure

error[E0502]: cannot borrow `errors` as immutable because it is also borrowed as mutable
  --> src/main.rs:96:8
   |
84 |     let mut push_auth_requirements = || {
   |                                      -- mutable borrow occurs here
85 |         errors.push(msg1.clone());
   |         ------ first borrow occurs due to use of `errors` in closure
...
96 |     if errors.is_empty() {
   |        ^^^^^^ immutable borrow occurs here
...
99 |             push_auth_requirements();
   |             ---------------------- mutable borrow later used here

How can I solve this? I need both closures to be able to modify the errors vec.


Solution

  • A closure that captures something need to save it in a field (of the closure), which causes the error, instead, pass errors as parameter:

    let mut errors: Vec<String> = Vec::new();
    
    let msg1 = "message 1".to_string();
    let msg2 = "message 2".to_string();
    let msg3 = "message 3".to_string();
    
    let mut push_auth_requirements = |errors: &mut Vec<_>| {
        errors.push(msg1.clone());
        errors.push(msg2.clone());
    };
    
    let mut push_auth_error = |errors: &mut Vec<_>| {
        errors.push(msg3.clone());
        push_auth_requirements(errors);
    };
    
    // do some stuff
    
    if errors.is_empty() {
        // do more stuff
        if "bob" == "dsadasd" {
            push_auth_requirements(&mut errors);
        } else {
            push_auth_error(&mut errors);
        }
    }
    
    println!("Errors: {:?}", errors);