Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why bother using `FnMut` if the argument is taken by value?

Tags:

closures

rust

Here is an example from Rust by Example:

pub trait Iterator {
    // The type being iterated over.
    type Item;

    // `any` takes `&mut self` meaning the caller may be borrowed
    // and modified, but not consumed.
    fn any<F>(&mut self, f: F) -> bool where
        // `FnMut` meaning any captured variable may at most be
        // modified, not consumed. `Self::Item` states it takes
        // arguments to the closure by value.
        F: FnMut(Self::Item) -> bool {}
}

Why bother using FnMut if the argument is taken by value, since the argument cannot be mutated anyways? In fact, why is FnMut even allowed here? It seems only FnOnce is permitted to do this:

It has been noted that Rust chooses how to capture variables on the fly without annotation. This is all very convenient in normal usage however when writing functions, this ambiguity is not allowed. The closure's complete type, including which capturing type, must be annotated. The manner of capture a closure uses is annotated as one of the following traits:

  • Fn: takes captures by reference (&T)
  • FnMut: takes captures by mutable reference (&mut T)
  • FnOnce: takes captures by value (T)
like image 825
qed Avatar asked Mar 25 '16 13:03

qed


2 Answers

The distinction between FnOnce, FnMut and Fn is how the function accesses its environment (move, mutable reference, shared reference, respectively). It has nothing to do with accessing the function's arguments.

FnMut is needed here, because the any method may need to call the function multiple times.

There is a paragraph in the Rust book about the implementation of closures. It shows the differences in the self argument, which essentially is a struct that contains the environment.

like image 109
starblue Avatar answered Nov 19 '22 15:11

starblue


Why bother use FnMut if the argument is taken by value, since the arg cannot be mutated anyways?

The example you linked is kind of incorrect, as the method definition should look like this:

fn any<F>(&mut self, mut f: F) -> bool  // note the `mut f: F`
    where F: FnMut(Self::Item) -> bool {}

(compare the std implementation)

This change, however, does not change how the any method can be used! The mut is merely part of the variable binding (which is only important for the method impl) and not part of the type. Taking something by value gives us complete control over the variable -- if we want to mutate the variable, we can do so, but we have to bind it mutably.


In fact, why is FnMut even allowed here?

You can have trait bounds that would allow all kinds of operations with a type, even if you can't use those operations due to lacking access to the variable. See:

fn foo<T: Iterator<Item=i32>>(it: &T) {}

This is valid, but not useful, because as soon as you want to do something usable with the iterator (like calling next):

it.next();

You will get a compiler error, since you can't call a &mut self method on an immutable borrow. So the example you linked only works, because the function body doesn't use the functionality of FnMut.

like image 23
Lukas Kalbertodt Avatar answered Nov 19 '22 17:11

Lukas Kalbertodt