This is because of the way closures are implemented in Rust.
You can think of a closure as a struct that gets automatically generated by the compiler and given some sort of call()
method.
When you make a move
closure, the compiler will find all the values that it refers to (i.e. p.i
) and move them into some closure's internal state. In this case, you are only referring to p.i
, which is an i32
, so you can think of the compiler as generating something like this:
struct Closure {
i: i32,
}
impl FnOnce<()> for Closure {
type Output = ();
fn call(mut self, args: ()) -> Self::Output {
self.i = 5;
}
}
When you look at it this way, it makes sense that doing p.i = 5
doesn't affect the original p
object. The compiler just made a copy of the value of p.i
and is updating that.
You can force the compiler to move the entire p
value into the closure by doing something like this:
f1(move || {
let mut p = p;
p.i = 5;
});
However now you'll get a "use of moved value" error because the p
value has been moved into the closure and can't be accessed by dbg!(p.i)
any more.
error[E0382]: use of moved value: `p`
--> src/main.rs:15:5
|
10 | let mut p = Point { i: 1 };
| ----- move occurs because `p` has type `Point`, which does not implement the `Copy` trait
11 | f1(move || {
| ------- value moved into closure here
12 | let mut p = p;
| - variable moved due to use in closure
...
15 | dbg!(p.i);
| ^^^^^^^^^ value used here after move
|
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0382`.
If you want the closure to be able to modify captured state, you'll probably want f1()
to accept FnMut
and not use move
.
struct Point {
i: i32,
}
fn f1(mut update: impl FnMut()) {
update()
}
fn main() {
let mut p = Point { i: 1 };
f1(|| {
p.i = 5;
});
dbg!(p.i);
}
(playground)