I think it's worth noting that the fact that this program fails to compile whereas the analogous Python runs but gives the wrong answer is exactly what Rust's ownership and borrowing system is about. Prior languages gave you either the ability to accidentally use invalid pointers (C, C++, etc.) or implicitly shared pointers (Python, Java, etc.). Rust, by sticking to only exclusive mutability (except when explicitly opted out of), prevents this mistake (using i
in the closure after it has been mutated elsewhere) by default.
(The particular error happened to be about &
reference lifetimes, not borrow conflicts, but Rust wouldn't have &
that works the way it does if it weren't for the ownership system.)
Of course, there are also other ways to solve this particular problem. Python's for
could have had semantics which declare a new i
variable (which each closure would capture separately) instead of the loop iterations assigning to a single existing variable.
But
i
shouldn't be borrowed here. Unless just copying the value ofi
intox
borrows it?
Non-move
closures always take the least powerful kind of capture (&
borrow < &mut
borrow < move) they can. Copying a value only requires a &
borrow, so that's what it picks. If i
was of a type that does not implement Copy
(say, a String
), then it would have been moved instead.
As already mentioned, using a move
closure is the solution. Beyond that, sometimes one finds the need to write explicit variable declarations to control exactly what a closure captures, somewhat like you tried ā but the key is that to be effective, those must be written outside the closure, not inside, and the closure must (for most of the cases where this arises) be a move
closure. That would look like:
vec.push({
let x = i.clone();
move || println!("message {}", x)
});
In this case, there's no reason to do that, since i
is Copy
and the move
closure suffices to handle that ā but I thought I'd mention this common pattern that you almost stumbled on.