Search code examples
rustclosureslifetimeborrow-checker

Closure may outlive the current function even if the lifetime is marked


fn get_closeures<'a>(x: &'a i64) -> Vec<Box<dyn Fn() + 'a>> {
    let mut ret: Vec<Box<dyn Fn() -> ()>> = Vec::new();
    ret.push(Box::new(|| println!("{}", x + 1)));
    ret.push(Box::new(|| println!("{}", x + 2)));

    ret
}

fn main() {
    let x: i64 = 100;
    {
        let closures = get_closeures(&x);
        for closure in closures {
            closure();
        }
    }
}

Playground

lead to the errors

error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
 --> src/main.rs:3:23
  |
3 |     ret.push(Box::new(|| println!("{}", x + 1)));
  |                       ^^                - `x` is borrowed here
  |                       |
  |                       may outlive borrowed value `x`
  |
note: closure is returned here
 --> src/main.rs:6:5
  |
6 |     ret
  |     ^^^
help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
  |
3 |     ret.push(Box::new(move || println!("{}", x + 1)));
  |                       ++++

My question is that I have mark the lifetime of the argument x stored must be longer than the closures stored into vector. Why does compiler stop me to borrow x?

I know that I can just add move keyword on the closure to fix it. But If x is not a i64 but a non Copy struct, then this method should not work.

Also, I think there is no memory problem inside my code, how to convince the rust compiler to believe it?


Solution

  • Note that x is not of type i64, but instead of type &i64, i.e. reference to i64. So x will always be Copy even if the underlying type isn't. Example:

    fn get_closeures<'a>(x: &'a str) -> Vec<Box<dyn Fn() + 'a>> {
        let mut ret: Vec<Box<dyn Fn() -> ()>> = Vec::new();
        ret.push(Box::new(move || println!("{}", x)));
        ret.push(Box::new(move || println!("{}", x)));
    
        ret
    }
    
    fn main() {
        let x: String = "azerty".to_string();
        {
            let closures = get_closeures(&x);
            for closure in closures {
                closure();
            }
        }
    }
    

    Playground