Closure as FnMut input parameter

Hi all,

I'm new to Rust and while working my way through rustbyexample.com, I've come across something I don't understand.

http://rustbyexample.com/fn/closures/input_parameters.html

// A function which takes a closure as an argument and calls
// it. The closure takes no input and returns nothing.
fn apply<F>(f: F) where
    F: FnMut() {
    // ^ TODO: Try changing this to 'Fn' or 'FnMut'.

    f()
}

// A function which takes a closure and returns an 'i32'.
fn apply_to_3<F>(f: F) -> i32 where
    // The closure takes an 'i32' and returns an 'i32'.
    F: Fn(i32) -> i32 {

    f(3)
}

fn main() {
    let greeting = "hello";
    // A non-copy type.
    let mut farewell = "goodbye".to_owned();

    // Capture 2 variables: 'greeting' by reference and
    // 'farewell' by value.
    let diary = || {
        // 'greeting' is by reference: requires 'Fn'.
        println!("I said {}.", greeting);

        // Mutation forces 'farewell' to be captured by
        // mutable reference. Now requires 'FnMut'.
        farewell.push_str("!!!");
        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzz");

        // Manually calling drop forces 'farewell' to
        // be captured by value. Now requires 'FnOnce'.
        //drop(farewell);
    };

    // Call the function which applies the closure.
    apply(diary);

    let double = |x| 2 * x;

    println!("3 doubled: {}", apply_to_3(double));
}
error: cannot borrow immutable local variable `f` as mutable

I commented out the drop, so the closure is of type FnMut and changed the annotation from FnOnce to FnMut. What am I missing?

To call a FnMut closure, f must be mutable. Use apply<F>(mut f: F) to make the f parameter mutable.

2 Likes

Thanks, that works. I thought I'd tried that, but apparently not.

It seems odd to require this syntax, as f is a reference to the closure which isn't being mutated. The FnMut trait annotation says variables within the closure will be captured by mutable reference or by immutable reference.

The closure is basically a struct of function pointer and the closed-over values. Thus to change the environment, you have to be able to mutate that struct.

Another way to see this: Calling the closure is conceptually the same as calling the method

f.call_mut(...arguments...)

of this trait, which has a &mut self parameter [1].

[1] note, to actually use that syntax at the moment you'll have to use nightly and a few annotations.

2 Likes

Why is the mut prefix not required for FnOnce, which can also mutate the contents?

Because FnOnce consumes self when called. At the trait level, you never need to indicate mut self for methods that consume the value - whether you need to actually mutate self or not will be a "local" concern, such as the trait implementer. To take a simple example:

trait Consume {
    fn foo(self);
}

impl Consume for String {
    fn foo(self) {
        self.clear();
    }
}

This won't compile because clear() needs to take a mutable borrow of self, but this is a local concern. This is fixed by changing the parameter to be mut self. The impl is still a valid Consume implementation.

2 Likes