Closure as FnMut input parameter


#1

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?


#2

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


#3

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.


#4

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.