Help needed - mutable borrow in a for loop

I am very new to rust and I'm struggling with the following code. From what I understand, this needs to be restructured somehow, but I don't know how.

Note that I can use an iterator instead of a for loop, but this is a simplified version of a bigger piece of code I'm working on where a loop is preferable to an iterator.

struct Rec {
    num: i32,
}

fn modify(data: &mut Vec<Rec>) -> Vec<&mut Rec> {
    let mut ret = Vec::<&mut Rec>::new();
    for j in 0 .. 10 { 
        let mut rec = data.get_mut(j).unwrap(); //<- error is here
        ret.push(rec);
    }
    ret
}

The error is:

error[E0499]: cannot borrow `*data` as mutable more than once at a time
  --> src/main.rs:10:23
   |
7  | fn modify<'a>(data: &'a mut Vec<Rec>) -> Vec<&'a mut Rec>{
   |           -- lifetime `'a` defined here
...
10 |         let mut rec = data.get_mut(j).unwrap();
   |                       ^^^^ `*data` was mutably borrowed here in the previous iteration of the loop
...
14 |     ret
   |     --- returning this value requires that `*data` is borrowed for `'a`

There's 2 questions:

  1. How to I restructure this. I have no idea
  2. Why doesn't the borrow end at the end of the code block for the for loop?

I've seen other posts offering assistance but I'm too much of a newbie to understand the advice given. Any help will be appreciated.

Once you call get_mut once, the compiler won't let you call it again while the previous result is still alive. Otherwise, you might get two copies of the same &mut T reference, which is strictly illegal. In this case it wouldn't happen since you pass a different index each time, but the compiler doesn't know that; it just follows simple rules with no exceptions.

Instead of get_mut, you can use an iterator, which is guaranteed to return each item at most once:

fn modify(data: &mut Vec<Rec>) -> Vec<&mut Rec> {
    let mut ret = Vec::<&mut Rec>::new();
    for rec in data.iter_mut().take(10) { 
        ret.push(rec);
    }
    ret
}
2 Likes

Some options:

  1. If the code can be proven to visit each item at most once, it can probably be rewritten to use iterators.
  2. If it might visit some items multiple times, you'll need to instead restructure it so that you don't have to stuff all those &mut references into the return value.
  3. For borderline cases, you can use types with interior mutability such as Cell and RefCell.
  4. If the code provably visits each item at most once, but none of the other suggestions will work, you might use unsafe instead to basically write your own iterator (this is how iter_mut() works internally, btw).

You don’t need manual unsafe for this, necessarily. iter_mut itself can be collected safely:

pub struct Rec {
    num: i32,
}

pub fn modify(data: &mut Vec<Rec>) -> Vec<&mut Rec> {
    let mut ret = Vec::<&mut Rec>::new();
    let mut split_data:Vec<Option<&mut Rec>> = data
        .iter_mut().map(|x| Some(x)).collect();
    for j in 0 .. 10 { 
        let rec = split_data[j].take().unwrap();
        ret.push(rec);
    }
    ret
}
1 Like

Often the reason I want a for loop is to use the index, in this case j.

It might help you to use enumerate, which gives you an index.

for (j, rec) in iter_mut().enumerate().take(10) {
    // some shenanigans with j
    ret.push(rec)
    // more shenanigans with j
}
1 Like

Thanks guys for all the suggestions. I'll definitely look into the split_data and enumerate solutions. If possible, I would like to avoid unsafe, at least for now

1 Like

This works. If I undstand this correctly, the mutable records (&mut Rec) are being wrapped but an immutable option. Since this is done with an iterator outside the loop, there's no double mutable borrow. Then, inside the loop, multiple (a total of 10) immutable borrows occur, but that's ok because they're immutable. By then unwrapping, I have the original data, but without making any mutable borrows in the loop. Is my understanding correct?

1 Like

Not quite. Fundamentally, what iter_mut does is take a single mutable borrow of a Vec<T> and turn it into a bunch of mutable borrows of the individual T's. Because these don't overlap with each other, they're all allowed to exist at the same time. The collect call builds a new vector containing all of these borrows so that you can access them later.

The Option wrapper is there to preserve the vector indices. Because &mut references can't be duplicated, you have to remove one from the vector in order to move it into the return value. You could do that with Vec::remove, but that would shift the later elements up to fill the empty space. Because tracking that correctly is hard, I filled the vector with Option<&mut Rec>s instead. Option::take removes the item and leaves a None in its place, which leaves all the other indices unchanged.

The borrows occur during the collect call before the loop, and the values remain borrowed as long as the function's return value continues to exist. The goal was to make all the borrows you'd need at the same time, because any one of them existing will prevent you from making any more.

1 Like

Still wrapping my head around it, but thanks!

1 Like

Here's a minor variant that shows the Option isn't necessary. Instead of a Vec, this version uses a BTreeMap to store the references:

pub struct Rec {
    num: i32,
}

use std::collections::BTreeMap;

pub fn modify(data: &mut Vec<Rec>) -> Vec<&mut Rec> {
    let mut ret = Vec::<&mut Rec>::new();
    let mut split_data:BTreeMap<usize, &mut Rec> = data
        .iter_mut().enumerate().collect();
    for j in 0 .. 10 { 
        let rec = split_data.remove(&j).unwrap();
        ret.push(rec);
    }
    ret
}

Along with all the answers given here, reading this stack overflow post might give you additional clarity.

The gist of the issue you are seeing can be seen by the following simplified example. Consider you have a function with signature as below -

fn some_func<'a>(data: &'a mut Vec<i32>, index: usize) -> &'a mut i32 {
    data.get_mut(index).unwrap()
}

If you call some_func like this, an error will be thrown at the line with the print statement.

fn main() {
    let mut a = vec![1,2,3,4];
    let b = some_func(&mut a, 1); // First mutable borrow occured here
    some_func(&mut a, 2); // Second mutable borrow occured here
    println!("{}", b); // Proof to the compiler that the first mutable borrow is later used.
}

The error thrown is -

error[E0499]: cannot borrow `a` as mutable more than once at a time
 --> src/main.rs:8:15
  |
7 |     let b = some_func(&mut a, 1);
  |                       ------ first mutable borrow occurs here
8 |     some_func(&mut a, 2);
  |               ^^^^^^ second mutable borrow occurs here
9 |     println!("{}", b);
  |                    - first borrow later used here

error: aborting due to previous error

The reason you're getting the above error is because of the function signature for some_func, which says that the lifetime of the resulting reference should be equal to the lifetime of the incoming reference. The only way it is possible (except for unsafe code) is that the resulting reference is somehow derived from the incoming reference, for example, it references some field inside the object the incoming reference points at.

In case of some_func we're saving the returned mutable reference from the first call in a variable b that is valid until the end of main function. This essentially lets the compiler to believe that we're holding a mutable reference to a until the end of main, which is why it does not like it when we try and borrow a, mutably, again before main ends.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.