“error: closure may outlive the current function”

2019-02-13 00:42发布

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.

2条回答
祖国的老花朵
2楼-- · 2019-02-13 01:20

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.

查看更多
冷血范
3楼-- · 2019-02-13 01:27

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);
    }
}
查看更多
登录 后发表回答