8

Closures have some data in their state, but how do I make it mutable? For example, I want a counter closure which returns the incremented value each time, but it doesn't work. How do I make it work?

fn counter() -> Box<Fn() -> i32> {
    let mut c: i32 = 0;
    Box::new(move || {
        c += 1;
        c
    })
}

fn main() {
    let mut a = counter();
    let mut b = counter();
    println!("{:?}", [a(), a(), a(), b(), b(), a()]);
}

Error (and warning) I'm getting:

error: cannot assign to captured outer variable in an `Fn` closure
        c += 1;
        ^~~~~~
help: consider changing this closure to take self by mutable reference
    Box::new(move || {
        c += 1;
        c
    })

I expect it to output something like [1, 2, 3, 1, 2, 4].

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Shchvova
  • 458
  • 5
  • 15

1 Answers1

12

As the error message says:

cannot assign to captured outer variable in an Fn closure

Instead, you want a FnMut closure:

// In modern Rust, prefer returning `impl FnMut() -> i32`
fn counter() -> Box<dyn FnMut() -> i32> {
    let mut c = 0;
    Box::new(move || {
        c += 1;
        c
    })
}

fn main() {
    let mut a = counter();
    let mut b = counter();

    let result = [a(), a(), a(), b(), b(), a()];
    println!("{:?}", result);

    assert_eq!([1, 2, 3, 1, 2, 4], result);
}

As the FnMut docs say:

The version of the call operator that takes a mutable receiver.

This allows the closure to mutate the contained state.

Incidentally, the explicit type for c is not needed.


What confuses me, is that pub trait Fn<Args>: FnMut<Args>. Doesn't it mean that Fn (what I used) should support behaviour of FnMut?

Perhaps When does a closure implement Fn, FnMut and FnOnce? can help provide some background information. This is an aspect I get intuitively, but haven't figured out how best to communicate. This section from Finding Closure in Rust also seems relevant:

At a high-level self gives implementers (i.e. the types users define to implement the trait) the most flexibility, with &mut self next and &self the least flexible. Conversely, &self gives consumers of the trait (i.e. functions with generics bounded by the trait) the most flexibility, and self the least.

In short, that trait definition says that any Fn closure can be used where a FnMut closure is expected. This makes some sense, as we can ignore the mutability available to the FnMut. You cannot go the other way - you cannot make an immutable reference into a mutable one.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • "any FnMut closure can be used as a Fn closure" other way around, yes? – Ry- Jan 27 '23 at 07:57
  • @Ry- it's all about which way you look at it from. I mention the trait **definition**, but you are looking at it from the trait **instantiation**. I'll see if I can clean up the wording. – Shepmaster Apr 16 '23 at 13:15