“error: closure may outlive the current function”

2019-02-13 00:44发布

问题:

When I try to compile the following code:

fn main() {

    (...)

    let mut should_end = false;

    let mut input = Input::new(ctx);

    input.add_handler(Box::new(|evt| {
        match evt {
            &Event::Quit{..} => {
                should_end = true;
            }
            _ => {}
        }
    }));

    while !should_end {
        input.handle();
    }
}

pub struct Input {
    handlers: Vec<Box<FnMut(i32)>>,
}

impl Input {
    pub fn new() -> Self {
        Input {handlers: Vec::new()}
    }
    pub fn handle(&mut self) {
        for a in vec![21,0,3,12,1] {
            for handler in &mut self.handlers {
                handler(a);
            }
        }
    }
    pub fn add_handler(&mut self, handler: Box<FnMut(i32)>) {
        self.handlers.push(handler);
    }
}

I get this error:

error: closure may outlive the current function, but it borrows `should_end`, which is owned by the current function

I can't simply add move to the closure, because I need to use should_end later in the main loop. I mean, I can, but since bool is Copy, it will only affect the should_end inside the closure, and thus the program loops forever.

As far as I understand, since input is created in the main function, and the closure is stored in input, it couldn't possibly outlive the current function. Is there a way to express to Rust that the closure won't outlive main? Or is there a possibility that I can't see that the closure will outlive main? In the latter case, it there a way to force it to live only as long as main?

Do I need to refactor the way I'm handling input, or is there some way I can make this work. If I need to refactor, where can I look to see a good example of this in Rust?

Here's a playpen of a simplified version. It is possible I made a mistake in it that could crash your browser. I happened to me once, so, beware.

In case it is needed, the rest of my code is available. All the relevant info should be in either main.rs or input.rs.

回答1:

The problem is not your closure, but the add_handler method. Fully expanded it would look like this:

fn add_handler<'a>(&'a mut self, handler: Box<FnMut(i32) + 'static>)

As you can see, there's an implicit 'static bound on the trait object. Obviously we don't want that, so we introduce a second lifetime 'b:

fn add_handler<'a, 'b: 'a>(&'a mut self, handler: Box<FnMut(i32) + 'b>)

Since you are adding the handler object to the Input::handlers field, that field cannot outlive the scope of the handler object. Thus we also need to limit its lifetime:

pub struct Input<'a> {
    handlers: Vec<Box<FnMut(i32) + 'a>>,
}

This again requires the impl to have a lifetime, which we can use in the add_handler method.

impl<'a> Input<'a> {
    ...
    pub fn add_handler(&mut self, handler: Box<FnMut(i32) + 'a>) {
        self.handlers.push(handler);
    }
}

Now all that's left is using a Cell to control access to your should_end flag.



回答2:

Here is an example of the fixed code:

use std::cell::Cell;

fn main() {
    let should_end = Cell::new(false);
    let mut input = Input::new();
    input.add_handler(Box::new(|a| {
        match a {
            1 => {
                should_end.set(true);
            }
            _ => {
                println!("{} {}", a, should_end.get())
            }
        }
    }));
    let mut fail_safe = 0;
    while !should_end.get() {
        if fail_safe > 20 {break;}
        input.handle();
        fail_safe += 1;
    }
}

pub struct Input<'a> {
    handlers: Vec<Box<FnMut(i32) + 'a>>,
}

impl<'a> Input<'a> {
    pub fn new() -> Self {
        Input {handlers: Vec::new()}
    }
    pub fn handle(&mut self) {
        for a in vec![21,0,3,12,1,2] {// it will print the 2, but it won't loop again
            for handler in &mut self.handlers {
                handler(a);
            }
        }
    }
    pub fn add_handler(&mut self, handler: Box<FnMut(i32) + 'a>) {
        self.handlers.push(handler);
    }
}