我想要对列表中的更改进行回调,因此我创建了一个简单的示例:
struct Foo;
struct FooList {
list: Vec<Foo>,
on_change_cb: Vec<Box<FnMut(& mut [Foo])>>,
}
impl FooList {
/*
pub fn register_on_change_cb2<F>(&mut self, cb: F) where F: FnMut(&mut [Foo]) {
self.on_change_cb.push(Box::new(cb));
}*/
pub fn register_on_change_cb(&mut self, cb: Box<FnMut(&mut [Foo])>) {
self.on_change_cb.push(cb);
}
pub fn push(&mut self, foo: Foo) {
self.list.push(foo);
self.on_change();
}
fn on_change(&mut self) {
for cb in &mut self.on_change_cb {
cb(&mut self.list);
}
}
}
我没有向编译器提供有关生命周期的任何明确提示:Vec<Box<FnMut(& mut [Foo])>>
,那么编译器在这里使用的生命周期是什么时候?如果我改变这样的代码:
struct FooList<'a> {
list: Vec<Foo>,
on_change_cb: Vec<Box<FnMut(&'a mut [Foo])>>,
}
impl<'a> FooList<'a> {
我收到编译时错误:
错误[E0495]:无法推断适当的借用生命周期 由于要求冲突而导致的表达
如何以某种方式显式设置生命周期,使得回调的& mut [Foo]
的生命周期小于,但不等于整个FooList
对象的生命周期?< / p>
我评论了register_on_change_cb2
,我想允许在不使用register_on_change_cb
的情况下调用Box::new
但是失败了。如果您取消注释register_on_change_cb2
,则会收到错误消息:
错误[E0310]:参数类型
F
的寿命可能不够长
如何在不需要'static
生命周期的回调的情况下修复此错误?我只想打电话给Box::new
。
答案 0 :(得分:3)
我将尝试回答你的问题1和3,因为问题2要么是多余的,要么与其他问题正交,我不知道你真正希望通过它实现什么。也许它应该有自己的问题。
如果您有一个需要引用的函数,但它不需要任何有关引用的生命周期信息,它必须能够接受任何生命周期的引用。这是明确的语法(这是编译器从您编写的代码中推断出来的):
on_change_cb: Vec<Box<for<'b> FnMut(&'b mut [Foo])>>,
这简称为higher ranked trait bound或HRTB。它们主要用于Fn
特征,这就是它们存在的原因。
如果on_change_cb
的类型为Vec<Box<FnMut(&mut [Foo])>>
,但不包含任何生命周期信息,则它不得包含任何引用('static
引用除外)。您需要说实现FnMut
的类型也可能包含(非'static
)引用,只要它们的生命周期延长'a
:
struct FooList<'a> {
list: Vec<Foo>,
on_change_cb: Vec<Box<FnMut(&mut [Foo]) + 'a>>,
}
这类似于:“对于每个FooList
对象,有一个生命周期'a
,使FooList
中的每个回调仅包含至少为{{1}生存的引用}“。这种解释可以更容易地为'a
编写原型:它需要回调也仅包含至少为register_on_change_cb2
生存的引用。
'a
(我认为我现在impl<'a> FooList<'a> {
pub fn register_on_change_cb2<F>(&mut self, cb: F)
where F: FnMut(&mut [Foo]) + 'a
{
self.on_change_cb.push(Box::new(cb));
}
的{{1}}正确 - 这个答案的先前版本错了。)
'a
生命周期让编译器保证您永远不会在'a
(以及Box
)中放回回,除非它至少持续Vec
本身。这很重要,因为闭包可以捕获对封闭范围中值的引用,如下面的代码(variance)所示:
FooList
在此示例中,您无法插入捕获let longlived = String::from("hello");
let mut list = FooList {
list: Vec::new(),
on_change_cb: Vec::new(),
};
list.register_on_change_cb2(|_| println!("{}", longlived)); // ok
let shortlived = String::from("hello");
list.register_on_change_cb2(|_| println!("{}", shortlived)); // `shortlived` does not live long enough
list.push(Foo);
的闭包,因为它不会超过(推断的)生命周期shortlived
。但是可以插入捕获'a
的闭包,因为编译器可以推断满足两个约束的生命周期longlived
:
'a
必须比'a
更长,因为list
的类型为list
。FooList<'a>
必须比longlived
更长,因为借用'a
的{{1}}在|_| println!("{}", longlived)
的调用中受longlived
限制。< / LI>
如果你想说回调没有借用引用任何,那么'a
生命周期是不必要的,在这种情况下你可以添加{{1}绑定playground link:
register_on_change_cb2