Reputation: 10252
What are the specific conditions for a closure to implement the Fn
, FnMut
and FnOnce
traits?
That is:
FnOnce
trait?FnMut
trait?Fn
trait?For instance, mutating the state of the closure on it's body makes the compiler not implement Fn
on it.
Upvotes: 239
Views: 74546
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
:
FnMut
:
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
:
(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
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
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 onceFnMut
(&mut self
) are functions that can be called if they have &mut
access to their environmentFn
(&self
) are functions that can be called if they only have &
access to their environmentA closure |...| ...
will automatically implement as many of those as it can.
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.FnMut
, allowing them to be called more than once (if there is unaliased access to the function object).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