2

I am trying to iterate over an array, mutably borrowing its elements one by one. In each iteration, I want to use the results from the previous one. I wrote the code like the one below:

fn main() {
    let mut arr: [Vec<u32>; 3] = [vec![1, 2, 3], Vec::new(), Vec::new()];
    for i in 1..3usize {
        let prev_vec: &Vec<u32> = &arr[i - 1];
        for prev_num in prev_vec {
            arr[i].push(prev_num * 2);
        }
    }
    dbg!(arr);
}

However, it seems that in the line let prev_vec: &Vec<u32> = &arr[i - 1]; instead of borrowing only the vector, the whole array gets marked as borrowed. The compiler errors is

 --> example.rs:6:13
  |
4 |         let prev_vec: &Vec<u32> = &arr[i - 1];
  |                                   ----------- immutable borrow occurs here
5 |         for prev_num in prev_vec {
  |                         -------- immutable borrow later used here
6 |             arr[i].push(prev_num * 2);
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

Why does the borrow checker assume the whole array is borrowed? How can I rewrite this code, so it is accepted by the compiler?

1 Answer 1

2

Indexing operations borrow the whole container - you can see that from the definition of the Index and IndexMut traits:

fn index(&self, index: Idx) -> &Self::Output;

The reason for this is that otherwise you could do something to the array that would invalidate the reference to the vec element. For example:

let e1 = arr[1];
arr.push(vec![9,8,7]);

Pushing something onto arr could result in it being re-allocated to a different area of memory, to accomodate the new element. That would mean e1 would be left pointing to nothing.

You can work around that using the split_at_mut method:

fn main() {
    let mut arr: [Vec<u32>; 3] = [vec![1, 2, 3], Vec::new(), Vec::new()];
    for i in 1..3usize {
        let (prev,curr) = arr.split_at_mut(i);
        let prev_arr = prev.last().unwrap();
        let this_arr = curr.first_mut().unwrap();
        for prev_num in prev_arr {
            this_arr.push(prev_num * 2);
        }
    }
    dbg!(arr);
}

Playground

split_at_mut takes a slice and gives you back two mutable sub-slices.

Sign up to request clarification or add additional context in comments.

1 Comment

"Pushing something onto arr could result in it being re-allocated to a different area of memory, to accomodate the new element." That's not the case here though since arr is an array not a vector. But I understand that the restriction comes from how Index trait is defined

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.