Lifetimes in closures with captured mutable reference

Why does using_for_loop compile but using_iterator doesn't.

struct A(i32);

struct B {
    arr: Vec<A>
}

impl B {
    fn using_iterator(&mut self) -> Vec<&A> {
        (0..2).map(|i| &self.arr[i]).collect()
    }
    
    fn using_for_loop(&mut self) -> Vec<&A> {
        let mut vec = Vec::with_capacity(2);
        for i in 0..2 {
            vec.push(&self.arr[i]);
        }
        vec
    }
}

It gives the following error

9 |         (0..2).map(|i| &self.arr[i]).collect()
  |                    ^^^  ---- `self` is borrowed here
  |                    |
  |                    may outlive borrowed value `self`

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=61fa3d9c3de68a4176b54253701a1622

I don't have an answer for your question, but for some reason it works when the methods take &self instead of &mut self as a parameter

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bc00d694c78784572bc3ec7a5fca9f26

This has to do with how closures are desugared, because the closure given to map stores a mutable reference to self. By making it store an immutable reference instead, it works:

fn using_iterator(&mut self) -> Vec<&A> {
    // this creates an immutable reference to self
    let this = &*self;
    (0..2).map(|i| &this.arr[i]).collect()
}

To be a bit more specific, it fails for the same reason that this fails:

struct MyStruct {
    field: String
}

impl MyStruct {
    fn borrow(&mut self) -> &str {
        &self.field
    }
}

fn main() {
    let mut s = MyStruct { field: "foo".to_string() };
    
    let a = s.borrow();
    let b = s.borrow();
    
    println!("{} {}", a, b);
}
error[E0499]: cannot borrow `s` as mutable more than once at a time
  --> src/main.rs:15:13
   |
14 |     let a = s.borrow();
   |             - first mutable borrow occurs here
15 |     let b = s.borrow();
   |             ^ second mutable borrow occurs here
16 |     
17 |     println!("{} {}", a, b);
   |                       - first borrow later used here

playground

In this example, your closure takes the role as MyStruct. Note that by changing borrow to take &self instead, the will start compiling again, which is similar to what I did with my immutable this variable.

3 Likes

@alice thank you for the explanation, I understand the reason now.
Isn't the original error message misleading though?

9 |         (0..2).map(|i| &self.arr[i]).collect()
  |                    ^^^  ---- `self` is borrowed here
  |                    |
  |                    may outlive borrowed value `self`

Yeah, the error message is not great.

How can we improve that, i'm not sure of the change process?

You can submit a bug report on the compiler's github repository.

1 Like

would that be here?

Yes.

Done

1 Like

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.