5

I have an array of Elements and I want to iterate over it to do some stuff, then iterate over all Elements inside the loop to do something. There is a relation between elements so I want to iterate to all other elements to check something. The elements are mutable references for reasons. It's a bit broad, but I'm trying to be general (maybe I should not).

struct Element;

impl Element {
    fn do_something(&self, _e: &Element) {}
}

fn main() {
    let mut elements = [Element, Element, Element, Element];

    for e in &mut elements {
        // Do stuff...

        for f in &mut elements {
            e.do_something(f);
        }
    }
}

As expected, I got this error:

error[E0499]: cannot borrow `elements` as mutable more than once at a time
  --> src/main.rs:13:18
   |
10 |     for e in &mut elements {
   |              -------------
   |              |
   |              first mutable borrow occurs here
   |              first borrow later used here
...
13 |         for f in &mut elements {
   |                  ^^^^^^^^^^^^^ second mutable borrow occurs here

I know it's a normal behavior in Rust, but what's the recommended way to avoid this error? Should I copy the elements first? Forget about loops and iterate in a different way? Learn about code design?

Is there a Rusty way to do this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
rap-2-h
  • 30,204
  • 37
  • 167
  • 263
  • You can't do this with iterators. I'd just use index based iteration (`for i in 0..elements.len()`) – oli_obk Sep 01 '17 at 15:15

2 Answers2

8

You can use indexed iteration instead of iterating with iterators. Then, inside the inner loop, you can use split_at_mut to obtain two mutable references into the same slice.

for i in 0..elements.len() {
    for j in 0..elements.len() {
        let (e, f) = if i < j {
            // `i` is in the left half
            let (left, right) = elements.split_at_mut(j);
            (&mut left[i], &mut right[0])
        } else if i == j {
            // cannot obtain two mutable references to the
            // same element
            continue;
        } else {
            // `i` is in the right half
            let (left, right) = elements.split_at_mut(i);
            (&mut right[0], &mut left[j])
        };
        e.do_something(f);
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
oli_obk
  • 28,729
  • 6
  • 82
  • 98
  • 2
    Note that this solution is subtly different from the OP's code, in the fact that `doSomething` will never be called with the same two values. The other answer describes why. – Shepmaster Sep 01 '17 at 15:54
4

You cannot do this, period. The rules of references state, emphasis mine:

At any given time, you can have either one mutable reference or any number of immutable references

On the very first iteration, you are trying to get two mutable references to the first element in the array. This must be disallowed.


Your method doesn't require mutable references at all (fn do_something(&self, e: &Element) {}), so the simplest thing is to just switch to immutable iterators:

for e in &elements {
    for f in &elements {
        e.doSomething(f);
    }
}

If you truly do need to perform mutation inside the loop, you will also need to switch to interior mutability. This shifts the enforcement of the rules from compile time to run time, so you will now get a panic when you try to get two mutable references to the same item at the same time:

use std::cell::RefCell;

struct Element;

impl Element {
    fn do_something(&mut self, _e: &mut Element) {}
}

fn main() {
    let mut elements = [
        RefCell::new(Element),
        RefCell::new(Element),
        RefCell::new(Element),
        RefCell::new(Element),
    ];

    for e in &elements {
        for f in &elements {
            // Note that this will panic as both `e` and `f` 
            // are the same value to start with
            let mut e = e.borrow_mut();
            let mut f = f.borrow_mut();
            e.do_something(&mut f);
        }
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • _Your method doesn't require mutable references at all_ What if it's actually `fn doSomething(&mut self, e: &mut Element) {}` and mutates both e and f. – Tuupertunut Jul 24 '19 at 01:56
  • @Tuupertunut that's what *You cannot do this, period* refers to, as does the section about the rules of references. Rust disallows two mutable references to the same item. – Shepmaster Jul 24 '19 at 01:59