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

时间:2017-05-24 14:42:21

标签: rust

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

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);
        }
    }
}
  1. 我没有向编译器提供有关生命周期的任何明确提示: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]:无法推断适当的借用生命周期   由于要求冲突而导致的表达

  2. 如何以某种方式显式设置生命周期,使得回调的& mut [Foo]的生命周期小于,但不等于整个FooList对象的生命周期?< / p>

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

      

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

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

1 个答案:

答案 0 :(得分:3)

我将尝试回答你的问题1和3,因为问题2要么是多余的,要么与其他问题正交,我不知道你真正希望通过它实现什么。也许它应该有自己的问题。

  1. 如果您有一个需要引用的函数,但它不需要任何有关引用的生命周期信息,它必须能够接受任何生命周期的引用。这是明确的语法(这是编译器从您编写的代码中推断出来的):

    on_change_cb: Vec<Box<for<'b> FnMut(&'b mut [Foo])>>,
    

    这简称为higher ranked trait bound或HRTB。它们主要用于Fn特征,这就是它们存在的原因。

    1. 如果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