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 indicate 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 a 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 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. (see: *FnOnce* example)
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
}
(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]
}
(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 no longer holds the ownership of a value it moved the ownership of the first time it was called.
println!("This is the vec: {:?}", get_that_same_vec()); // this does not compile
}