Closure may outlive the current function

Hi,

I cannot understand the error closure may outlive the current function. Here both factor and closure(borrowing factor) are defined/called in the same scope (main function).

fn main() {
    let factor = 10;
    let multiplier = get_multiplier(factor);

    println!("{}", multiplier(2));
}

fn get_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
    |input| input * factor
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0373]: closure may outlive the current function, but it borrows `factor`, which is owned by the current function
 --> src/main.rs:9:5
  |
9 |     |input| input * factor
  |     ^^^^^^^         ------ `factor` is borrowed here
  |     |
  |     may outlive borrowed value `factor`
  |
note: closure is returned here
 --> src/main.rs:9:5
  |
9 |     |input| input * factor
  |     ^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `factor` (and any other referenced variables), use the `move` keyword
  |
9 |     move |input| input * factor
  |     ++++

For more information about this error, try `rustc --explain E0373`.
error: could not compile `playground` due to previous error

Well, the closure is created inside get_multiplier, but it continues to live in the main function after get_multiplier returns.

To understand why you get this error, well, it's because the compiler generates the following code from your closure:

struct AutoGeneratedClosureType<'a> {
    factor: &'a i32,
}

impl<'a> AutoGeneratedClosureType<'a> {
    fn call(&self, value: i32) -> i32 {
        let factor = *self.factor;
        value * factor
    }
}

fn get_multiplier(factor: i32) -> AutoGeneratedClosureType<'_> {
    AutoGeneratedClosureType {
        factor: &factor,
    }
}

As you can see, you are trying to return a struct containing a reference to factor, which is a local variable in get_multiplier. To fix it, you add move to the closure:

 fn get_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
-    |input| input * factor
+    move |input| input * factor
 }

This makes the compiler take ownership of factor rather than take a reference. (The i32 is moved into the closure.) The generated code will look like this:

struct AutoGeneratedClosureType {
    factor: i32,
}

impl AutoGeneratedClosureType {
    fn call(&self, value: i32) -> i32 {
        value * self.factor
    }
}

fn get_multiplier(factor: i32) -> AutoGeneratedClosureType {
    AutoGeneratedClosureType {
        factor: factor,
    }
}
5 Likes

If you redefine get_multiplier like this, it will work:

fn get_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
    move |input| input * factor
}

Your original version captures a reference to the local variable factor. This is freed at the end of get_multiplier, leaving a dangling reference inside the closure.

So If I rewrite first case in the following manner

fn get_multiplier(factor: i32) -> AutoGeneratedClosureType<'static> {
    let ptr = &factor; 
    AutoGeneratedClosureType {
        factor: ptr,
    }
}

then ptr is the variable owned by get_multiplier but we are returning a reference of it to the main function which will not be available as ptr will be removed from the stack. Is my understanding correct?

No, the issue is not that ptr is removed from the stack. The issue is that factor is removed from the stack. You may be confused about the fact that factor also exists as a variable in main, however, when you call get_multiplier, the get_multiplier function gets its own copy of factor. That copy is destroyed when get_multiplier returns.

In fact, if you change the code such that the reference points into the original factor variable owned by main, then it will work:

fn main() {
    let factor = 10;
    // note `&` here
    let multiplier = get_multiplier(&factor);

    println!("{}", multiplier.call(2));
}

// note `&` here
fn get_multiplier(factor: &i32) -> AutoGeneratedClosureType<'_> {
    AutoGeneratedClosureType {
        factor: factor,
    }
}

click to see full code

3 Likes

Ah!! Got it. Let me know if I understood correctly

  1. Since we are using a function to build a closure, and passing factor(lets call it ownedFactor) as owned value to the function (i32 in this case )

  2. creating |input| input * factor closure by default captures value by reference

  3. And we are returning closure which is having reference to ownedFactor which will produce an error as ownedFactor will be dropped after function returns and reference inside closure will be a dangling pointer.

1 Like

Yep, and you need to explicitly use the move keyword if you want to move ownership of factor into the closure.

Exactly :ok_hand:

1 Like