I have the following snippet of code:
fn f<T: FnOnce() -> u32>(c: T) {
println!("Hello {}", c());
}
fn main() {
let mut x = 32;
let g = move || {
x = 33;
x
};
g(); // Error: cannot borrow as mutable. Doubt 1
f(g); // Instead, this would work. Doubt 2
println!("{}", x); // 32
}
Doubt 1
I can not run my closure even once.
Doubt 2
... but I can invoke that closure as many times as I want, provided that I call it through f
. Funnily, if I declare it FnMut
, I get the same error as in doubt 1.
Doubt 3
What does self
refer to in Fn
, FnMut
and FnOnce
traits definition? Is that the closure itself? Or the environment?
E.g. from the documentation:
pub trait FnMut<Args>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
Some basics about the Fn*
trait family is needed to understand how closures actually work. You have the following traits:
FnOnce
, which, as the name implies, can only be run once. If we look at the docs page we'll see that the trait definition is almost the same as what you specified in your question. What is most important though, is the following: The "call" function takes self
, meaning that it consumes the object which implements FnOnce
, so like any trait function which takes a self
as a parameter, it takes ownership of the object. FnMut
, which allows for mutation of the captured variables, or in other words, it takes &mut self
. What this means, is that when you make a move || {}
closure, it will move any variables you reference which are outside the scope of the closure into the closure's object. The closure's object has a type which is unnameable, meaning that it is unique to each closure. This does force the user to take some kind of mutable version of the closure, so &mut impl FnMut() -> ()
or mut x: impl FnMut() -> ()
Fn
, which is generally considered the most flexible. This allows the user to take an immutable version of the object implementing the trait. The function signature for this trait's "call" function is the simplest to understand of the three, as it only takes a reference to the closure, meaning that you don't need to worry about ownership while passing it around or calling it. To address your individual doubts:
move
something into a closure, the variable is now owned by the closure. Essentially, what the compiler generates is like the following pseudocode:struct g_Impl {
x: usize
}
impl FnOnce() -> usize for g_Impl {
fn call_once(mut self) -> usize {
}
}
impl FnMut() -> usize for g_Impl {
fn call_mut(&mut self) -> usize {
//Here starts your actual code:
self.x = 33;
self.x
}
}
//No impl Fn() -> usize.
And by default it calls the FnMut() -> usize
implementation.
Copy
as long as each of their captured variables are Copy
, meaning that the closure that is generated will be copied into f
, so that f
ends up taking a Copy
of it. When you change the definition for f
to take an FnMut
instead, you get the error because you are facing a similar situation to doubt 1: You are trying to call a function which receives &mut self
while you've declared the parameter to be c: T
instead of either mut c: T
or c: &mut T
, either of which qualify for &mut self
in the eyes of FnMut
. self
parameter is the closure itself, which has captured or moved some variables into itself, so it now owns them.