错误:闭包可能超出当前函数的生命周期,但不会超出它。

23

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

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是在主函数中创建的,并且闭包存储在input中,它不可能超出当前函数的生命周期。有没有办法向Rust表达闭包不会超出main的生命周期?或者我可能看不到闭包将超出main的生命周期吗?在后一种情况下,有没有办法强制其仅存在于main的生命周期中?

我需要重新处理输入的方式吗,还是有办法使其工作。如果需要重新处理,则可以在Rust中查看哪些示例?

这里是简化版本的Playpen。我可能犯了一个错误,可能会导致浏览器崩溃。我曾经遇到过这样的情况,所以要小心。

如果需要,其余的代码都可用。所有相关信息应该在main.rsinput.rs中。

2个回答

17

问题不在于你的闭包,而在于add_handler方法。完全展开后,它看起来像这样:

问题不在于您的闭包,而是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>>,
}

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

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

现在唯一剩下的就是使用 Cell 控制访问你的 should_end 标志。


我只需要阅读关于Cell等内容的资料。除此之外,非常详尽的答案,谢谢。 - MrNosco
这个隐式的 'static 是从哪里来的,有没有记录在某个地方? - starblue
如果你没有指定生命周期,那么除了函数参数和本地绑定之外,它的生命周期通常为“static”,这一点需要注意。显然,函数参数和本地绑定可以推断出更短的生命周期。 - oli_obk
这个主题有一个相关的RFC:https://github.com/rust-lang/rfcs/blob/master/text/1214-projections-lifetimes-and-wf.md - oli_obk
你为什么写成了 'b: 'a 而不是只写成 'b 呢?这个限制的理论基础是什么? - Nawaz

1
这里是编程代码的一个例子:

固定代码示例


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接