Denilson Amorim
Denilson Amorim

Reputation: 10252

When does a closure implement Fn, FnMut and FnOnce?

What are the specific conditions for a closure to implement the Fn, FnMut and FnOnce traits?

That is:

For instance, mutating the state of the closure on it's body makes the compiler not implement Fn on it.

Upvotes: 239

Views: 74546

Answers (3)

endvvell
endvvell

Reputation: 573

Seeing that right now there is only one other answer here that might not make the content of the question entirely clear to some, as it did not for me, I'll provide a few examples together with some reasoning that helped me understand what these closure traits are all about:

— Why do we need them?: "traits are how functions and structs can specify what kinds of closures they can use and store inside of them (as with the structs)"

— What each of them indicates about a closure:

  • Fn:

    • Does not move the ownership of the captured values out of its scope (for example, to the caller of a closure).
    • Either does not capture any values or does not mutate the captured values.
    • Because they do not affect the ownership of the underlying memory of the values they capture (i.e. "work with"), nor the state of the values they capture, Rust allows calling these closures multiple times (because calling them is an entirely safe operation).
  • FnMut:

    • Does not move the ownership of the captured values out of its scope.
    • Does, in fact, capture values from the environment in which it is called and mutates the state of those values. (That "and" is important, if it wasn't for it, then we would be working with a closure that only captures the values and does not alter them in any way, and that is what Fn trait is used to indicate. As op noted: "mutating the state of the closure on it's body makes the compiler not implement Fn on it.")
  • FnOnce:

    • Does, in fact, move the ownership of the captured values out of its scope.
    • Either does not capture any values or captures and uses them in such a way that it would be unsafe to call this function multiple times (since it might, for example — if it were allowed to be called multiple times — cause multiple owners of the same underlying memory of those values it moves the ownership of out of its scope). (That "or" is important: all closures implement this trait since they can be called at least once, but if this trait is the only one they implement and no other, then they can only be called once.)

      ^^ "When does a closure implement only this trait and no other?": when it moves the ownership of the captured values out of its scope, because the other two traits are, by their very definition, not implemented by a closure if it moves the ownership of the captured values out of its scope.

(All these traits are just a way for the compiler to determine where it can allow an arbitrary closure to be used while keeping the memory of the data the closure operates on in a "safe" state)

— Some examples:

(note: I cannot annotate the updated_vec let binding with an "impl" type, so I will specify a type inferred by the rust-analyzer in a comment near the binding's definition)

  • Fn trait:
fn main() {
    let some_vec = vec![1, 3, 4];
    
    let get_that_same_vec = || { // "get_that_same_vec" type: impl Fn() -> &Vec<i32>
        &some_vec
        // as you can see the closure is specified to implement the *Fn* trait,
        // meaning it can be called however many times since it doesn't alter the data it operates on
        // or pass the ownership of that data to any other entity 
    };
    // - By using the "&" above we are basically saying that a closure should just return a reference to a value.
    // - If we were to omit the "&" above, we would basically be saying:
    // "return and pass the ownership of this value to whomever decides to invoke this closure"
    // which would basically be like turning it into an infinite generator of that value and its ownership
    // "Oh, another caller wants this value and the ownership of it?
    // Sure, let me take the copy of that value... out of thin air I guess!"
    // As you can figure, Rust doesn't allow for that last sentence to be true,
    // since that would allow multiple entities to have the ownership of the underlying memory,
    // which would eventually result in a "double free" error when needing to free that underlying memory
    // when one of the owners goes out of scope. (*FnOnce* example demonstrates this)   

    println!("This is the vec: {:?}", get_that_same_vec());
    println!("This is the vec: {:?}", get_that_same_vec()); // if "&" would not be present above, then this would not compile
}
  • FnMut:

(On why we need to mark the variable that holds an FnMut closure with a mut see this great answer)

fn main() {
    let mut some_vec = vec![1, 3, 4];

    let mut update_vec = || { // "update_vec" type: impl FnMut()
        some_vec.push(5); 
    };

    // As you can see the closures that implement the *FnMut* trait can be called multiple times,
    // because they do not pass the ownership of the data they capture out of their scope
    // they only alter its state, and if altering the value of its state multiple times is a legal operation
    // for a type on which the closure operates, then it is surely ok to call such a closure multiple times  
    update_vec();
    update_vec();
    println!("This is the updated \"some_vec\": {:?}", some_vec);
    // This is the updated "some_vec": [1, 3, 4, 5, 5]
}
  • FnOnce:

(all I did here was: I removed the & in front of some_vec inside of the closure in the Fn example)

fn main() {
    let some_vec = vec![1, 3, 4];
    
    let get_that_same_vec = || { // "get_that_same_vec" type: impl FnOnce() -> Vec<i32>
        some_vec
        // as you can see the closure is specified to implement the *FnOnce* trait,
        // rust-analyzer shows only the most relevant trait that a closure implements
        // meaning that, in this case, a closure is marked as such that can only be called once,
        // since it passes the ownership of the data it captures to another entity.
        // In this case, that entity is the "get_that_same_vec" variable.
    };

    println!("This is the vec: {:?}", get_that_same_vec());
    // the call to println below does not compile and throws error "value used here after move",
    // and the way the compiler is able to infer this is by knowing
    // that a closure that implements only the `FnOnce` trait and no other trait
    // can only be called once, it (the closure) no longer holds the ownership of the value it moved the ownership of
    // during that first time it was called.
    println!("This is the vec: {:?}", get_that_same_vec()); // this does not compile
}

Upvotes: 17

Hydroper
Hydroper

Reputation: 433

Here is a resume for @huon and @svitanok answers, emphasizing that Fn and FnMut (and additional types) do extend FnOnce, but they are used separately:

fn main() {
    let f: Box<dyn Fn()> = Box::new(|| { println!("foo"); });
    f();
    f();
    let mut f: Box<dyn FnMut()> = Box::new(|| { println!("foo"); });
    f();
    f();
    let f: Box<dyn FnOnce()> = Box::new(|| { println!("foo"); });
    f();
    f(); // compile error!
}

Upvotes: 0

huon
huon

Reputation: 102066

The traits each represent more and more restrictive properties about closures/functions, indicated by the signatures of their call_... method, and particularly the type of self:

  • FnOnce (self) are functions that can be called once
  • FnMut (&mut self) are functions that can be called if they have &mut access to their environment
  • Fn (&self) are functions that can be called if they only have & access to their environment

A closure |...| ... will automatically implement as many of those as it can.

  • All closures implement FnOnce: a closure that can't be called once doesn't deserve the name. Note that if a closure only implements FnOnce, it can be called only once.
  • Closures that don't move out of their captures implement FnMut, allowing them to be called more than once (if there is unaliased access to the function object).
  • Closures that don't need unique/mutable access to their captures implement Fn, allowing them to be called essentially everywhere.

These restrictions follow directly from the type of self and the "desugaring" of closures into structs; described in my blog post Finding Closure in Rust.

For information on closures, see Closures: Anonymous Functions that Can Capture Their Environment in The Rust Programming Language.

Upvotes: 247

Related Questions