Understanding &mut self and closures error

I'm having some issues understanding these closure / lifetime errors and would like if someone could explain:

fn main() {
    let mut container = MyContainer { value: 0 };
    let value_ref = container.populate_value_and_get();
    println!("{}", value_ref);
}

struct MyContainer {
    value: i32
}

impl MyContainer {
    // error #1
    // fn populate_value_and_get(&mut self) -> &i32 {
    //     self.value = 42;
    //     let closure = || self.get_value();
    //     closure()
    // }

    // error #2
    // fn populate_value_and_get(&mut self) -> &i32 {
    //     self.value = 42;
    //     let closure = move || self.get_value();
    //     closure()
    // }

    // this works
    fn populate_value_and_get(&mut self) -> &i32 {
        self.value = 42;
        let self_ref = &*self;
        let closure = || self_ref.get_value();
        closure()
    }

    fn get_value(&self) -> &i32 {
        &self.value
    }
}

I have 3 versions of MyContainer::populate_value_and_get. The first two have errors I don't understand. Can someone break it down for me? :slight_smile:

Is same as:

|| &(self.get_value())

Is same as:

|| {
    let tmp: &i32 = self.get_value();
    &tmp // &&i32
}

You can't return a reference to the local variable.

1 Like

Oops, I left it as &self.get_value() when I meant self.get_value() for the errors I didn't understand. I edited the OP

The first error is:

error[E0373]: closure may outlive the current function, but it borrows `self`, which is owned by the current function
  --> src\main.rs:15:23
   |
15 |         let closure = || self.get_value();
   |                       ^^ ---- `self` is borrowed here
   |                       |
   |                       may outlive borrowed value `self`
   |
note: closure is returned here
  --> src\main.rs:16:9
   |
16 |         closure()
   |         ^^^^^^^^^
help: to force the closure to take ownership of `self` (and any other referenced variables), use the `move` keyword  
   |
15 |         let closure = move || self.get_value();
   |                       ^^^^^^^

and the second error is:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src\main.rs:22:36
   |
22 |         let closure = move || self.get_value();
   |                                    ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 22:23...
  --> src\main.rs:22:23
   |
22 |         let closure = move || self.get_value();
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `self`
  --> src\main.rs:22:31
   |
22 |         let closure = move || self.get_value();
   |                               ^^^^
note: but, the lifetime must be valid for the anonymous lifetime defined on the method body at 20:31...
  --> src\main.rs:20:31
   |
20 |     fn populate_value_and_get(&mut self) -> &i32 {
   |                               ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src\main.rs:23:9
   |
23 |         closure()
   |         ^^^^^^^^^

It you introduce an explicit lifetime in the method definition, and use it in the closure return type too you get a better error message

fn populate_value_and_get<'a>(&'a mut self) -> &'a i32 {
    self.value = 42;
    let closure = || -> &'a i32 { self.get_value() };
    closure()
}
error[E0597]: `self` does not live long enough
  --> src/main.rs:15:39
   |
13 |     fn populate_value_and_get<'a>(&'a mut self) -> &'a i32 {
   |                                    --               -- also has lifetime `'a`
   |                                    |
   |                                    has lifetime `'a`
14 |         self.value = 42;
15 |         let closure = || -> &'a i32 { self.get_value() };
   |                                       ^^^^ `self` would have to be valid for `'a`...
16 |         closure()
17 |     }
   |     - ...but `self` will be dropped here, when the function `populate_value_and_get` returns
   |
   = help: use data from the highlighted arguments which match the `'a` lifetime of the return type
   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#dangling-references>

Essentially the closure is creating a new borrow of self and that means the lifetime of the returned reference doesn't match the implicit lifetime on the method's return type. I believe that's why your example with the explicit reborrow works, it decouples the lifetime of the returned reference from self.

Note: explicitly using 'a in the closure return type may actually be slightly different from what the compiler would normally infer, but I think it illustrates what's happening behind the scenes in a useful way

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.