Loop with &mut reference

I made a struct like

struct Items<T> {
    items: Vec<Option<T>>,
}

, and I encountered multiple mutable reference with this

fn get_mut(&mut self, start: usize, value: &T) -> Option<&mut T> {
    let len = self.items.len()
    for i in 0..len {
        // `self.items[index]` must have value, so this unwrapping never fails.
        match self.items.get_mut((start + i) % len).unwrap() {
                         ^^^^^^^
            Some(v) => if v == value { return Some(v) }
                                                   ^
            None => {}
        }
    }
}

This mutably borrows self.items but the reference will be released at the end of match in any iteration.
It returns only ONE reference in the iterations, so it seems ok.
What's the point?

This is a known limitation of the borrow checker. Your code is okay, there's no reason that the borrow checker complains except that the borrow checker isn't smart enough to accept this use case. It's related to how the lifetime of the borrow of self.items that's passed to get_mut differs on subsequent branching paths. On one path, a reference depending on this borrow escapes the function via the return; on another path, it must end before the next usage of self.items in the next loop iteration.

Possible solutions[1] are listed e.g. also here, along with more context on what's the general issue in the first place; in this case, you can help out the compiler by the relatively minor restructuring of re-doing the get_mut call in the returning branch, so that on non-returning branches, no [even potentially] long-lasting borrows are created in the first place:

let j = (start + i) % len;
// `self.items[index]` must have value, so this unwrapping never fails.
match self.items.get_mut(j).unwrap() {
    Some(v) => {
        if v == value {
            return self.items.get_mut(j).unwrap().as_mut();
        }
    }
    None => {}
}

Rust Playground


  1. including the most 'advanced' approach of incorporating some helper functionality of that crate itself ↩ī¸Ž

3 Likes

Specifically this problem I would solve with an iterator based approach

let len = self.items.len();
let (part_2, part_1) = self.items.split_at_mut(start % len);
part_1.iter_mut().filter_map(Option::as_mut).find(|x| *x == value).or_else(||
    part_2.iter_mut().filter_map(Option::as_mut).find(|x| *x == value)
)
2 Likes

Good idea; I would further consider .chaining the two iterators, avoiding some code duplication

let len = self.items.len();
let (part_2, part_1) = self.items.split_at_mut(start % len);
let reordered = part_1.iter_mut().chain(part_2);
reordered.filter_map(Option::as_mut).find(|x| *x == value)
3 Likes

Thank you. I was relieved my code is logically valid.
I expect there should be an issue about this borrow checker limitation. Do you know one?

It's a very old and well-known one.

1 Like

Thanks!

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.