6

In C, I can mutably iterate over an array in a nested fashion, using indices. In Rust, I can pretty much do the same using indices, but what if I want to use an iterator instead of indices?

The following piece of code for example compiles successfully since both borrows are immutable:

let xs = [0, 1, 2];
for x in &xs {
    for y in &xs {
        println!("x={} y={}", *x, *y);
    }
}

But what if I want to use a mutable iterator?

let mut xs = [0, 1, 2];
for x in &mut xs {
    *x += 1;
    for y in &mut xs {
        *y += 1;
        println!("x={} y={}", *x, *y);
    }
}

This results in:

error[E0499]: cannot borrow `xs` as mutable more than once at a time

I understand the need to channel write access to data, but I also wonder how an experienced Rust user would achieve that using only iterators -- let's say indices are out of the picture just for educational purposes.

qwe
  • 766
  • 1
  • 5
  • 15
  • 1
    `while let` can be used for this purpose, as pointed out in [this thread](https://stackoverflow.com/questions/29859892/mutating-an-item-inside-of-nested-loops) – Misty May 18 '21 at 20:03
  • 3
    [This article](https://pr0.uk/rust/programming/loop/borrow-checker/2019/09/02/rust-inner-loop.html) tackles this problem in depth. – cdhowie May 19 '21 at 07:35
  • 2
    There is no way to do that with iterators, because at some time `x` and `y` point to the same item in `xs` and are used to mutate this item. The only workaround aside from using indexing, which you exclude, is to change `xs` into a vector of [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html)s to limit the extent of the mutable access. – Jmb May 19 '21 at 07:36

1 Answers1

4

I understand the need to channel write access to data, but I also wonder how an experienced Rust user would achieve that using only iterators

They would not because it's not feasible, at least not as-is: while desugaring to while let is a common method, it'd be for nesting loops where you want to advance the same iterator in the outer and inner loop, it avoids the need for an exclusive borrow spanning the entire loop.

Here though, you don't want to advance the same iterator in different places, instead you want two different mutating iterators to the same collection. Meaning you have two different mutable references to the same collection (which is not allowed) and you would eventually have two mutable references to the same item of a collection (which is also not allowed).

At best, you could use interior mutability to resolve the issue e.g.

let xs = [Cell::new(0), Cell::new(1), Cell::new(2)];
for x in &xs {
    x.set(x.get() + 1);
    for y in &xs {
        y.set(y.get() + 1);
        println!("x={} y={}", x.get(), y.get());
    }
}

but I don't think that would be the usual choice (falling back to indices would be, as it would not require changing the data itself).

Masklinn
  • 34,759
  • 3
  • 38
  • 57