Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a closure that has mutably captured a variable in a loop that checks the same value?

Tags:

closures

rust

fn main() {
    let mut foo = 1;

    let mut func = || foo += 1;
    while foo < 5 {
        func();
    }
}
error[E0503]: cannot use `foo` because it was mutably borrowed
 --> src/main.rs:5:11
  |
4 |     let mut func = || foo += 1;
  |                    -- borrow of `foo` occurs here
5 |     while foo < 5 {
  |           ^^^ use of borrowed `foo`

I understand why this isn't working, but I'm looking for a way to bypass the borrow checker somehow. Is there a way to use a closure here? Is there a good alternative besides using a function? I've got a situation where I have to change several variables.

like image 531
bertfred Avatar asked Oct 26 '25 08:10

bertfred


2 Answers

One option you have is to pass a mutable reference to the closure rather than implicitly borrowing it by environment capture:

fn main() {
    let mut foo = 1;

    let func = |foo: &mut i32| *foo += 1;

    while foo < 5 {
       func(&mut foo);
    }
}

playground

like image 157
Jorge Israel Peña Avatar answered Oct 28 '25 21:10

Jorge Israel Peña


No, you cannot do this. While the closure has the mutable borrow, nothing else may access that variable.

Instead...

Runtime mutability XOR aliasing enforcement

You can use a Cell or a RefCell:

use std::cell::Cell;

fn main() {
    let foo = Cell::new(1);

    let func = || foo.set(foo.get() + 1);

    while foo.get() < 5 {
        func();
    }
}

See also:

  • Situations where Cell or RefCell is the best choice
  • Right way to share a reference between closures
  • Execute callbacks like as mutable borrowing from cycle
  • Passing a closure that modifies its environment to a function in Rust
  • Problems with mutability in a closure
  • When I can use either Cell or RefCell, which should I choose?

Let the closure handle everything

You can bake the comparison into the closure:

fn main() {
    let mut foo = 1;

    let mut func = || {
        foo += 1;
        foo < 5
    };

    while func() {}
}

Use a struct

struct Thing(i32);

impl Thing {
    fn do_it(&mut self) {
        self.0 += 1
    }

    fn test_it(&self) -> bool {
        self.0 < 5
    }
}

fn main() {
    let mut foo = Thing(1);

    while foo.test_it() {
        foo.do_it();
    }
}
like image 28
Shepmaster Avatar answered Oct 28 '25 22:10

Shepmaster