Reputation: 21
I am having trouble understanding why a particular pattern is not compiling.
Rust recognizes when I move a variable and then reassign to it outside of a closure and I think properly allows the code to compile, but when I try to do the same in a closure that will be run more than once it will not.
fn main() {
let mut v = vec![1, 2, 3, 4];
v.into_iter().fold(0, |a, b| a + b);
v = vec![1, 2, 3, 4];
vec![1, 2, 3].into_iter().for_each(|x| {
v.into_iter().fold(x, |a, b| a + b);
v = vec![1, 2, 3, 4];
});
}
error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
--> src/main.rs:6:9
|
2 | let mut v = vec![1, 2, 3, 4];
| ----- captured outer variable
...
6 | v.into_iter().fold(x, |a, b| a + b);
| ^ cannot move out of captured outer variable in an `FnMut` closure
It seems to me that the reassignment to v
should satisfy the borrow checker that no variable will be accessed after being moved. Am I missing something?
Upvotes: 2
Views: 526
Reputation: 299730
As @Shepmaster mentioned, the fix is to use std::mem::replace
.
So, what is the difference between:
v.into_iter().fold(x, |a, b| a + b);
v = vec![1, 2, 3, 4];
and:
let v_old = std::mem::replace(&mut v, vec![1, 2, 3, 4]);
v_old.into_iter().fold(x, |a, b| a + b);
?
In two words: exception safety.
If, for some reason, the expression v.into_iter().fold(...)
would panic, it would leave v
moved out and the next statement would never be executed.
This is perfectly acceptable in a FnOnce
, as you will never call the closure another time, but not acceptable in a FnMut
or Fn
as on the next call... what would you do with v
?
On the other hand, using std::mem::replace
, you swap first and then execute the potentially panicking operation. If the operation does panic, then all that is left "moved out" is a temporary variable which disappears at the end of the stack frame anyway. No issue.
Upvotes: 4
Reputation: 430310
the reassignment to
v
should satisfy the borrow checker that no variable will be accessed after being moved
Pay attention to the error message details — there isn't a move to start with:
cannot move out of captured outer variable in an `FnMut` closure
Since there was no move out, it doesn't make sense to move something back in.
Instead, you can replace the value through the mutable reference and consume the old value:
fn main() {
let mut v = vec![1, 2, 3, 4];
vec![1, 2, 3].into_iter().for_each(move |x| {
let v_old = std::mem::replace(&mut v, vec![1, 2, 3, 4]);
v_old.into_iter().fold(x, |a, b| a + b);
});
}
Upvotes: 2