Search code examples
rustclosuresmutableborrowing

A variable modified by two closures


Consider the following (contrived) way to increment x by 9.

fn main() {
  let mut x = 0;
  let mut f = || {
    x += 4;
  };
  let _g = || {
    f();
    x += 5;
  };
}
error[E0499]: cannot borrow `x` as mutable more than once at a time
 --> x.rs:6:12
  |
3 |   let mut f = || {
  |               -- first mutable borrow occurs here
4 |     x += 4;
  |     - first borrow occurs due to use of `x` in closure
5 |   };
6 |   let _g = || {
  |            ^^ second mutable borrow occurs here
7 |     f();
  |     - first borrow later captured here by closure
8 |     x += 5;
  |     - second borrow occurs due to use of `x` in closure

error: aborting due to previous error

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

So, it does not work. How to make an algorithm like the above that would modify a variable in a closure and call another closure from it that also modifies the variable?

In traditional languages it's easy. What to do in Rust?


Solution

  • By design, closures have to enclose external objects that are used in it upon creation, in our case closures have to borrow the external x object. So as compiler explained to you, upon creation of the closure f it mutably borrows x, and you can't borrow it once again when you create closure g.

    In order to compile it you can't enclose any external object that you want to change. Instead you can directly pass the objects as a closure's argument (arguably it's even more readable). This way you describe that a closure accepts some object of some type, but you don't yet use/pass any actual object. This object will be borrowed only when you call the closure.

    fn main() {
      let mut x = 0;
      let f = |local_x: &mut i32| { // we don't enclose `x`, so no borrow yet
        *local_x += 4;
      };
      let _g = |local_x: &mut i32| { // we don't enclose `x`, so no borrow yet
        f(local_x);
        *local_x += 5;
      };
      _g(&mut x); // finally we borrow `x`, and this borrow will later move to `f`, 
                  // so no simultaneous borrowing.
      println!("{}", x); // 9
    }