首页 文章

Vec切片对话的明确生命周期

提问于
浏览
0

我想要对列表中的更改进行回调,因此我创建了一个简单的示例:

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 对象的生命周期?

  • 我评论了 register_on_change_cb2 ,我想允许在不使用 Box::new 的情况下调用 register_on_change_cb 但是失败了 . 如果取消注释 register_on_change_cb2 ,则会收到错误消息:

错误[E0310]:参数类型F的寿命可能不够长

如何在不需要 'static 生命周期的回调的情况下修复此错误?我只想打电话给 Box::new .

1 回答

  • 3

    我将尝试回答你的问题1和3,因为问题2要么是多余的,要么与其他问题正交,我无法说出你真正想要实现的目标 . 也许它应该有自己的问题 .

    • 如果您有一个带引用的函数,但它不需要有关引用的任何生命周期信息,它必须能够接受 any lifetime的引用 . 这是明确的语法(这是编译器从您编写的代码中推断出来的):
    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 中的每个回调都只包含至少为 '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));
        }
    

    (我想我现在 'a'a 正确 - 这个答案的先前版本错了 . )

    'a lifetime让编译器保证你永远不会在 Box (因此 Vec )中放回一个回调,除非它至少和 FooList 本身一样长 . 这很重要,因为闭包可以捕获对封闭范围中值的引用,如下面的代码(playground link)所示:

    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 更长,因为借用 longlived|_| println!("{}", longlived)register_on_change_cb2 的调用中受到 'a 的限制 .

    如果你想说回调没有通过引用借用任何东西,那么 'a 生命周期是不必要的,在这种情况下你可以添加the compiler suggests绑定the compiler suggests

    pub fn register_on_change_cb2<F>(&mut self, cb: F)
            where F: FnMut(&mut [Foo]) + 'static
    

相关问题