I'm trying to make some sections of my code easier to read by using small helper closures.
fn main() {
let mut loop_index = 0;
let get_src_index = || return loop_index % 2;
let get_dst_index = || return (loop_index + 1) % 2;
loop_index += 1;
}
There's a few more closures that make use of get_src_index()
and get_dst_index()
, but they are not important to the problem. The problem is that I can no longer modify loop_index
:
error[E0506]: cannot assign to `loop_index` because it is borrowed
--> src/main.rs:6:5
|
4 | let get_src_index = || return loop_index % 2;
| -- borrow of `loop_index` occurs here
5 | let get_dst_index = || return (loop_index + 1) % 2;
6 | loop_index += 1;
| ^^^^^^^^^^^^^^^ assignment to borrowed `loop_index` occurs here
I don't quite understand why Rust doesn't allow incrementing the loop_index
variable. Is there a more "Rusty" way of doing this?
As the error message says, you can't change the value because it's borrowed. It's the same as if you had said:
let mut loop_index = 0;
let foo = &loop_index;
loop_index += 1;
You aren't allowed to modify a value while there is an outstanding immutable borrow. This is a fundamental concept in Rust and underpins the safety it provides.
Why does the closure have a reference to the variable? That's the entire point of closures — to capture the environment. Closures infer how to capture the variables based on the operations taking place inside, and in this case a reference is sufficient.
This is normally what you want because a reference is easy to pass around. In this case, you have numbers so there's no reason to prefer the reference. We can use the move
keyword to move the number into the closure. Since numbers implement Copy
, this will make a copy. Then the closure is completely separate from the existing value:
let mut loop_index = 0;
let get_src_index = move || loop_index % 2; // No need for `return`
let get_dst_index = move || (loop_index + 1) % 2;
loop_index += 1;
However, I don't think that these particular closures really buy you anything. Integer operations are generally cheap and conditionals are often less cheap, so doing an extra computation might be better.
If you needed the ability to modify the loop_index
and have that change reflected inside the closures, you could use a Cell
:
use std::cell::Cell;
fn main() {
let mut loop_index = Cell::new(0);
let get_src_index = || loop_index.get() % 2;
let get_dst_index = || (loop_index.get() + 1) % 2;
loop_index.set(loop_index.get() + 1);
}
Even better, in many cases you don't need to maintain your own loop index anyway. Iterator::enumerate
takes care of it for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With