“错误:关闭可能比当前功能更长”但它不会比它活得更久

时间:2016-02-26 12:06:23

标签: closures rust lifetime

当我尝试编译以下代码时:

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);
    }
}

我收到此错误:

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

我不能简单地将move添加到闭包中,因为我需要稍后在主循环中使用should_end。我的意思是,我可以,但由于boolCopy,它只会影响闭包内的should_end,因此程序会永远循环。

据我所知,由于input是在main函数中创建的,而闭包存储在input中,因此它不可能比当前函数寿命更长。有没有办法向Rust表达封闭不会比main更长寿?或者是否有可能我看不到封闭会比main更长?在后一种情况下,有一种方法可以强制它只在main生效吗?

我是否需要重构我处理输入的方式,或者是否有某些方法可以使其工作。如果我需要重构,我在哪里可以看到Rust的一个很好的例子?

这是a playpen of a simplified version。我可能在其中犯了一个可能导致浏览器崩溃的错误。我碰巧遇到过一次,所以,要小心。

如果需要,my code is available的其余部分。所有相关信息均应位于main.rsinput.rs

2 个答案:

答案 0 :(得分:10)

问题不是关闭,而是add_handler方法。完全展开它看起来像这样:

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

正如您所看到的,特征对象上存在隐式'static绑定。显然我们不想要那样,所以我们引入了第二个生命周期'b

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

由于您要将handler对象添加到Input::handlers字段,因此该字段不能超过handler对象的范围。因此,我们还需要限制其生命周期:

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

这再次要求impl具有生命周期,我们可以在add_handler方法中使用它。

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

现在剩下的就是使用Cell来控制对should_end标记的访问。

答案 1 :(得分:0)

以下是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);
    }
}