Li Zichao
Li Zichao

Reputation: 11

Mutable borrow in multiple closures

I have two closure that reference and modify an outer variable.

let mut i: i32 = 0;

let mut add = || {
    i += 1;
};
let mut sub = || {
    i -= 1;
};
add();
sub();

The following code won't compile that produces the error below:

error[E0499]: cannot borrow `i` as mutable more than once at a time
  --> src/main.rs:14:19
   |
11 |     let mut add = || {
   |                   -- first mutable borrow occurs here
12 |         i += 1;
   |         - first borrow occurs due to use of `i` in closure
13 |     };
14 |     let mut sub = || {
   |                   ^^ second mutable borrow occurs here
15 |         i -= 1;
   |         - second borrow occurs due to use of `i` in closure
16 |     };
17 |     add();
   |     --- first borrow later used here

How to use mutable borrow in multiple closures?

Upvotes: 0

Views: 85

Answers (2)

Lilian Bideau
Lilian Bideau

Reputation: 307

restructuring your code a little bit it works!

fn main() {
    let mut i: i32 = 0;

    let add = |mut j| {
        j += 1;
        j
    };
    let sub = |mut j| {
       j -= 1;
       j
    };
    i = add(i);
    i = sub(i);
    println!("{}", i); // 0
}

Upvotes: -1

user2722968
user2722968

Reputation: 16535

The way you described it, you can't. The add-closure takes an exclusive ("mutable") borrow of i at the point where the closure is constructed. The compiler guarantees that add is the only way to observe i until that borrow ends. Yet sub does exactly the same thing: It tries to borrow i exclusively, and that borrow is used after the call to add(). But there is no way that both

  • add has exclusive access to i via it's mutable borrow
  • sub has an exclusive borrow of i, which is exclusive while add is being called, so it can be used by sub after add has been called

can be true at the same time.

Depending on your use-case, and if you can't avoid this problem altogether, either restructure your code:

fn main() {
    let mut i: i32 = 0;

    let mut add = || {
        i += 1;
    };

    add();

    // The mutable borrow by `add` has ended, so `i` is free to get borrowed again

    let mut sub = || {
        i -= 1;
    };

    sub();
}

or use a RefCell to avoid having to use mutable borrows:

use std::cell::RefCell;

fn main() {
    let i: RefCell<i32> = RefCell::new(0);

    let add = || {
        *i.borrow_mut() += 1;
    };

    let sub = || {
        *i.borrow_mut() -= 1;
    };

    add(); // 1
    sub(); // 0
    add(); // 1
    add(); // 2

    assert_eq!(*i.borrow(), 2);
}

Notice that by using a RefCell, the bindings add/sub are no longer declared mut, as the closures use immutable ("shared") borrows of i; there is a runtime-check embedded in RefCell that checks if i is in fact exclusively borrowed while the closures execute. For instance, it is conceivable that sub calls add while it is executing, leading to a situation that is statically guarantee to not occur in the code you pasted: Two aliasing + mutable access paths to the same variable; as written above, RefCell would panic! to guarantee at runtime that this does not occur.

Upvotes: 0

Related Questions