Reputation: 886
let array = [40];
let mut var = 60;
for element in array.iter().filter(|&x| {*x < var}) {
var += 1; // Error
}
var += 1; // Fine again
To me this code seems completely legit, as the closure should be over by the time I actually access var
outside of it.
error[E0506]: cannot assign to `var` because it is borrowed
--> src/main.rs:6:9
|
5 | for element in array.iter().filter(|&x| {*x < var}) {
| ---- borrow of `var` occurs here
6 | var += 1; // Error
| ^^^^^^^^ assignment to borrowed `var` occurs here
Why is var
still borrowed when calling var += 1
even though the scope of the closure should be already over? The result is needed to get to var += 1
. While it is possible to do something like this without filter
, it causes my code to be a lot less clear, so I would like to keep using it.
Upvotes: 2
Views: 84
Reputation: 432089
the closure should be over by the time I actually access
No. Iterators are lazy. That means that the order of operations here is:
next
on the combined iterator.next
on the iterator from the array. Repeat this step until the condition is passed.You are capturing val
inside the filter's closure. You also try to modify it in the loop. This would mean that there has to be a mutable reference and an immutable one at the same time, which is disallowed.
You could use Cell
to have interior mutability:
use std::cell::Cell;
fn main() {
let array = [40];
let var = Cell::new(60);
for element in array.iter().filter(|&&x| x < var.get()) {
var.set(var.get() + 1);
}
let mut var = var.get();
var += 1;
}
Upvotes: 4
Reputation: 22273
Shepmaster's answer is correct (I didn't know iterators were that lazy), but if you don't want the var
check (i.e. the filter
condition) to change during the loop, you can use a move
closure:
filter(move |&x| *x < var)
Since i32
implements Copy
, the value of var
will just be copied for the purposes of the closure.
Upvotes: 2