如何在结构体上存储回调函数列表?

3
这是对我之前问题的跟进:如何创建和使用回调函数列表
我正在尝试创建(并在事件循环附近存储)一个回调函数列表,在将来某个不确定的时间点调用它们。
struct ComplexThing {
    calls: Vec<Box<FnMut()>>,
}


impl ComplexThing {
    fn call<'a, T: FnMut() + 'a>(&'a mut self, func: T) {
        self.calls.push(Box::new(func));
    }
}

出现错误:

calls.rs:30:25: 30:39 error: the parameter type `T` may not live long enough [E0310]
calls.rs:30         self.calls.push(Box::new(func));
                                    ^~~~~~~~~~~~~~
calls.rs:30:39: 30:39 help: consider adding an explicit lifetime bound `T: 'static`...
calls.rs:30:25: 30:39 note: ...so that the type `T` will meet its required lifetime bounds
calls.rs:30         self.calls.push(Box::new(func));
                                    ^~~~~~~~~~~~~~

我尝试将其添加到struct中,这样就可以解决对push调用的生命周期错误了。

struct ComplexThing<'a> {
    calls: Vec<Box<FnMut() + 'a>>,
}


impl ComplexThing {
    fn call<'a, T: FnMut() + 'a>(&'a mut self, func: T) {
        self.calls.push(Box::new(func));
    }
}

...但是给我的结果是:

calls.rs:28:6: 28:18 error: wrong number of lifetime parameters: expected 1, found 0 [E0107]
calls.rs:28 impl ComplexThing {
                 ^~~~~~~~~~~~

是的,我想结构体有一个 <'a>,但我没有指定它。如果我添加它,

impl ComplexThing<'a> {

我明白了,

calls.rs:28:19: 28:21 error: use of undeclared lifetime name `'a` [E0261]
calls.rs:28 impl ComplexThing<'a> {

我不确定是否应该在 struct ComplexThing 上指定它。如果我去掉它(我非常希望这样做,我想),

我认为Rust对生命周期的符号表示中有一些至关重要的东西我没有理解。 ComplexThing (目前) 尝试存储在一个 Box 中的 FnMut 是由 ComplexThing 的实例所拥有的; 它的生命周期应该比 .ComplexThing 的生命周期短 - 即两件事情中的一件会发生:

  1. ComplexThing 的一个私有函数将删除 Vec 中的 Box<FnMut> (因此获得所有权),运行 FnMut,然后退出,因此释放 FnMut
  2. ComplexThing 被释放,在这种情况下,Vec 和任何 Box<FnMut> 也将被释放。

这个问题的"答案"让我想到Box<FnMut>不需要生命周期注释,但是我在如何创建和使用回调列表中得到的答案让我认为我需要。

我的猜测是Box只是存储一个指向我并不真正拥有的对象的指针,我需要在堆上创建FnMut的副本或者将其移动构造到堆上,然后我才能拥有那个副本。(否则,如果它像在栈上的闭包一样,我需要确保在我的Box之前,闭包不会超出作用域,这就是为什么Rust要求我注释生命周期的原因。)


Box<Trait>Box<Trait + 'static> 的语法糖,这完全解释了第一个错误。顺便说一下,&'a Trait&'a (Trait + 'a) 的语法糖。 - Chris Morgan
1个回答

3

ComplexThing 上的 'a 是一个通用的生命周期参数,需要像通用类型参数一样进行定义。如果你有一个 struct Foo<T>,你将无法编写 impl Foo<T>,因为在作用域中没有具体的类型 T;如果你想让它成为通用的,你需要定义它,impl<T> Foo<T>。这也允许你写约束条件,如 impl<T: Clone> Foo<T> 来实现只在 T 是实现了 Clone 的类型上实现方法。

因此,答案很简单,你需要将生命周期 'a 定义为通用的:

impl<'a> ComplexThing<'a> { … }

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