Can't borrow vec elements twice

In my problem I have a vector-based computations that modifies its element based on the value of its other elements. The following example of a running sum may serve as a simplified toy problem:

#[derive(Copy, Clone)]
struct X{x:i16}

fn running_sum(a:&X, b:&X, c:&mut X) {
    c.x = a.x + b.x;
}

fn main() {
    const N: usize = 10
    let mut row = [X{x:0};N];
    for i in 0..N-2 {
        running_sum(&row[i], &row[i+1], &mut row[i+2]);
    }
    println!("{} {} {}", &row[0].x, &row[1].x, &row[2].x);
}

It simply evaluates x[i] = x[i-1] + x[i-2]. This obviously doesn't work, because:

error[E0502]: cannot borrow `row[_]` as mutable because it is also borrowed as immutable
  --> src/main.rs:11:35
   |
11 |     running_sum(&row[0], &row[1], &mut row[2]);
   |     ----------- -------           ^^^^^^^^^^^ mutable borrow occurs here
   |     |           |
   |     |           immutable borrow occurs here
   |     immutable borrow later used by call

I fully understand the reason; the question is how to make it compile?

Please don't advice putting the c.x = a.x + b.x; into the for loop of the main program. This is exactly the situation I have now. In my real case that inner calculation involves a lot of math and I want to refactor it by breaking into parts.

The only solution (not specific to rust) that comes to my mind is to refactor running_sum() into: running_sum(row: &mut &[X], pos: usize);

Any rust-specific solution that would solve the double-borrow problem?

Here’s a way to make it work:

for i in 0..N-2 {
    let [a, b, c] = &mut row[i..i+3] else {
        unreachable!();  // ^^^^^^^^
    };                   //   +-- or: `row[i..][..3]`
    running_sum(a, b, c);
}

In the future, for convenient and fully flexible access to multiple elements of a vec/array/slice, we might also eventually be able to use the (still unstable) get_many_mut API.

Stable solutions of accessing multiple elements of a slice mutably[1] at the same time, include slice patterns as demonstrated in the code above, and the more flexible split_at_mut method, which can help achieve the same for non-subsequent elements of a slice.[2]


  1. or partially mutably ↩︎

  2. For example, here’s one way to use it in this code example:

    for i in 0..N-2 {
        let (row_init, row_i_plus_2) = row.split_at_mut(i+2);
        running_sum(&row_init[i], &row_init[i+1], &mut row_i_plus_2[0]);
    }
    
    ↩︎
6 Likes

Another alternative.

    let mut borrow = &mut row[..];
    while let [a, b, c, ..] = borrow {
        running_sum(a, b, c);
        borrow = &mut borrow[1..];
    }

This is basically an inlined mut version of windows, which can't exist as a standard Iterator due to the aliasing. But there may be a lending iterator crate that provides it.

5 Likes

The other solutions are better in this particular case, but I recently had to do a similar computation where the elements were not contiguous in the array. In that case I used split_at_mut(x, i).