Mutably borrowed here in the previous iteration of the loop

I'm struggling to understand why this code snippet leads to borrowed in previous loop.

use std::ops::Range;

pub struct MutView<'a, MT> where MT: Storage {
    data: &'a mut MT,
    range: Range<usize>,
}

pub trait Storage {
    fn len(&self) -> usize;
}

impl<'a, MT: Storage> MutView<'a, MT> {

    fn mut_slice(&'a mut self, range: Range<usize>) -> MutView<'a, MT> {
        MutView {
            data: self.data,
            range,
        }
    }

    pub fn new(storage: &'a mut MT) -> MutView<'a, MT> {
        let le = storage.len();
        MutView {
            data: storage,
            range: 0..le
        }
    }

}

impl Storage for Vec<i32> {
    fn len(&self) -> usize {
        self.len()
    }
}

pub struct RunStateView<'a, T: Storage> {
    pub x: MutView<'a, T>,
}

fn main() {
    let mut stor = vec![1];
    let mut rsv = RunStateView { x: MutView::new(&mut stor) };
    for _i in 0..3 {
        let cd = &mut rsv.x.mut_slice(0..1);

    }
}

and the error

50 |         let cd = &mut rsv.x.mut_slice(0..1);
   |                       ^^^^^
   |                       |
   |                       `rsv.x` was mutably borrowed here in the previous iteration of the loop
   |                       first borrow used here, in later iteration of loop

The logic here is, I have storage data structure say Vec and I want to create various mutable views on top of it. Inside a loop, I am updating some portion of the underlying Vec.

Why would the previous borrow persists in the next loop? There is no later reference to the mut reference?

Code in playground here.

The issue here is this signature:

impl<'a, MT: Storage> MutView<'a, MT> {

    fn mut_slice(&'a mut self, …

that’s an instance of the anti-pattern &'a mut SomeType<'a> (in the type of self). Such a reference, where the reference’s lifetime is the same as a lifetime inside of the type and the reference is mutable, is essentially never what you want. Once you create it, the value is borrowed mutable for it’s entire duration of existence, since the borrow lives as long as the value itself can exist (due to the lifetimes being the same). In the loop, you try to borrow it like this twice which thus fails. (Types that implement Drop[1] would not even permit such a borrowing once.

This signature is probably motivated by compiler errors that suggested the change, so the underlying problem is likely something else. (Often, the attempt to create self-referencing data structures.)

(It’s a bit unfortunate that the compiler still isn’t improved to avoid generating suggested fixes that create this anti-pattern. Assuming this was the case here to begin with.)

I haven’t checked the code further yet to say.


  1. or have other drop glue accessing the lifetime ↩︎

2 Likes

Have you read @quinedot 's guide (link)? The mut_slice method looks a little fishy. Its return value has the same lifetime as the original object which is very restrictive.

2 Likes

So upon second look (and a minute of trying out changes to the signature of mut_slice) my conclusion is that this might have been a case of a different way to accidentally arrive at &'a mut SomeType<'a>: not paying attention and accidentally using too few different lifetimes in your code.

With a different lifetime

    fn mut_slice<'b>(&'b mut self, range: Range<usize>) -> MutView<'b, MT> {
        MutView {
            data: self.data,
            range,
        }
    }

the code compiles fine.

(Unless of course, if the code is somehow simplified / reduced, and some usage actually required the more restrictive signature.)

This signature also works with lifetime elision, due to how lifetime elision preferres lifetimes of self arguments:

    fn mut_slice(&mut self, range: Range<usize>) -> MutView<'_, MT> {
        MutView {
            data: self.data,
            range,
        }
    }

Rust Playground

2 Likes

Thank you @steffahn @user16251 ! I also checked the quinedot, super helpful.

Once you create it, the value is borrowed mutable for it’s entire duration of existence, since the borrow lives as long as the value itself can exist (due to the lifetimes being the same).

My takeaway on this is, even though the variable cd goes out of scope after the current loop, the function signature of mut_slice says the mutable borrow will last for the entire duration of the existence of rsv.x, thus compiler will complain on the 2nd loop. Is this the right understanding?

It's the right understanding. Though note that it's even worse than "2nd time through the loop" -- you can't use rsv.x directly ever again, because it's forever exclusively borrowed. That includes using the rsv as a whole (obtaining a &rsv or &mut rsv or moving rsv[1]). Examples.

References going out of scope doesn't generally mean much lifetime-wise. If you have a borrow of the reference itself (e.g. let x = &cd), those can't outlast the reference going out of scope. But any of the lifetimes in the reference's type can be longer than the scope.


  1. you could still use other fields, if it had any ↩︎

1 Like

References going out of scope doesn't generally mean much lifetime-wise
TIL, super helpful.

Yeah...I read your post on that (mentioned by @user16251 ) and the whole thing is exclusive borrowed. Feeling progressed quite a bit on understanding lifetime from this discussion.

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.