Why I can't mutate a value between two closure calls that also mutate that value?

Hello!

Take a look at this code:

fn main() {
    let mut val = 0;
    
    let mut closure = || {
        val += 1;
    };

    closure();

    val += 1;

    closure();

    println!("{}", val);
}

The code fails to compile with the following error:

error[E0503]: cannot use `val` because it was mutably borrowed
  --> src/main.rs:10:5
   |
4  |     let mut closure = || {
   |                       -- `val` is borrowed here
5  |         val += 1;
   |         --- borrow occurs due to use of `val` in closure
...
10 |     val += 1;
   |     ^^^^^^^^ use of borrowed `val`
11 |
12 |     closure();
   |     ------- borrow later used here

Why the code doesn't work? And how it is different from the following, working code?

fn main() {
    let mut val = 0;
    
    fn closure(v: &mut i32) {
        *v += 1;
    }

    closure(&mut val);

    val += 1;

    closure(&mut val);

    println!("{}", val);
}

I intended to use a closure in order to simplify code. I have some repetitive complex lines (in place of val += 1) in my real code that use variables from the current function scope. Using a function instead of a closure would require me to pass a lot of variables in that function, which would take a lot of space in the code, and I'm trying to minimize it.

Is there a way to mutate a variable between two closure calls that also mutate that variable?

Your code is equivalent to this:

struct MyClosure<'a> {
    val: &'a mut i32,
}

impl<'a> MyClosure<'a> {
    fn call(&mut self) {
        *self.val += 1;
    }
}

fn main() {
    let mut val = 0;
    
    let mut closure = MyClosure {
        val: &mut val,
    };

    closure.call();

    val += 1;

    closure.call();

    println!("{}", val);
}

This fails to compile because the closure holds a mutable reference:

error[E0503]: cannot use `val` because it was mutably borrowed
  --> src/main.rs:21:5
   |
16 |         val: &mut val,
   |              -------- `val` is borrowed here
...
21 |     val += 1;
   |     ^^^^^^^^ use of borrowed `val`
22 |
23 |     closure.call();
   |     -------------- borrow later used here

You could use a macro instead?

fn main() {
    let mut val = 0;
    
    macro_rules! inc_val {
        () => {
            val += 1;
        };
    }
    
    inc_val!();

    val += 1;

    inc_val!();

    println!("{}", val);
}
4 Likes

Another option is to use a RefCell:

use std::cell::RefCell;

fn main() {
    let val = RefCell::new(0);
    
    let closure = || {
        *val.borrow_mut() += 1;
    };

    closure();

    *val.borrow_mut() += 1;

    closure();

    println!("{}", *val.borrow_mut());
}

RefCell is more often seen in long-lived structures, but it can be used temporarily like this too.

1 Like

In this case you'd want Cell:

use std::cell::Cell;

fn main() {
    let val = Cell::new(0);
    
    let closure = || {
        val.set(val.get() + 1);
    };

    closure();

    val.set(val.get() + 1);

    closure();

    println!("{}", val.get());
}

The Cell type is really cool because it interacts nicely with mutable references like this:

use std::cell::Cell;

fn uses_val(val: &mut i32) {
    let val = Cell::from_mut(val);
    
    let closure = || {
        val.set(val.get() + 1);
    };

    closure();

    val.set(val.get() + 1);

    closure();

    println!("{}", val.get());
}
2 Likes