【问题标题】:How do you create an iterator using a closure in rust?如何在 rust 中使用闭包创建迭代器?
【发布时间】:2015-01-07 18:23:43
【问题描述】:

我天真地尝试这样做:

struct Foo<'a, S: Send, T:Send> {
    next_:Box<Fn<(&'a mut S,), Option<T>> + Send>,
    state:S
}

impl<'a, S: Send, T: Send> Iterator<T> for Foo<'a, S, T> {
    fn next(&mut self) -> Option<T> {
        return self.next_.call((&mut self.state,));
    }
}

要创建迭代器,我可以使用闭包轻松发送到任务。

但是,它会产生可怕的生命周期不匹配错误:

<anon>:8:33: 8:48 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
<anon>:8         return self.next_.call((&mut self.state,));
                                         ^~~~~~~~~~~~~~~
<anon>:7:5: 9:6 help: consider using an explicit lifetime parameter as shown: fn next(&'a mut self) -> Option<T>
<anon>:7     fn next(&mut self) -> Option<T> {
<anon>:8         return self.next_.call((&mut self.state,));
<anon>:9     }
error: aborting due to previous error
playpen: application terminated with error code 101

我不明白这个错误。

闭包应该接受一个生命周期为 'a 的参数,即结构的生命周期。

状态归结构所有,因此它的生命周期为 'a。

使用 next_.call((&mut self.state,)) 不会调用任务;它应该只在 call() 期间,据我所知应该是有效的。

所以这里的不匹配是在 next() 中 self 的生命周期和调用中的 'a 之间......但我不明白为什么它不会是 'a。

修复上述代码的正确方法是什么?

有没有更好的方法来做到这一点?

游戏围栏:http://is.gd/hyNi0S

【问题讨论】:

    标签: rust


    【解决方案1】:

    这需要higher-rank lifetimes,因为闭包中的生命周期不应该是类型签名的一部分:闭包只想将&amp;'a mut S 用于任何生命周期'a(因为它需要使用仅保证在 next 方法内部持续存在的数据调用函数:外部无法命名),而不是在类型签名中外部暴露(并且有些可控)的生命周期。这是不可能的,但我已经看到 Niko Matsakis 谈论在 IRC 上工作,并且有像 #18837 这样的准备性拉取请求,所以希望很快就会出现。

    需要明确的是:代码失败是因为 next_ 只能使用至少存在 'a 的引用来调用,但 &amp;mut self.state 只能存在与 &amp;mut self 一样长的时间,而不是 'a除非它被声明为&amp;'a mut self(这就是编译器建议它的原因)。添加此生命周期是非法的,因为它不满足特征声明的要求。

    你现在可以使用旧的闭包来解决这个问题(这本质上就是 Fn 特征对象),甚至还有一个标准库类型可以为你做这件事:Unfold

    【讨论】:

    【解决方案2】:

    这是当前 Rust 特征系统的一个不幸限制,很快就会解除。缺少higher-kinded lifetimes。据我所知,目前正在实施它。

    让我们仔细检查您的结构定义(我已删除 mut 以允许对 'static 进行进一步推理,这并没有使其不那么普遍):

    struct Foo<'a, S: Send, T:Send> {
        next_: Box<Fn<(&'a S,), Option<T>> + Send>,  // '
        state: S
    }
    

    'a 生命周期参数在这里是一个输入 参数。这意味着它是由结构的用户提供的,而不是由它的实现者提供的。

    (顺便说一句,它不是结构实例的生命周期 - 你不能只用类型参数指定这样的生命周期;它必须是 self 引用的生命周期,你也可以'不使用,因为Iterator trait 方法没有生命周期参数。不过,这只是一个旁注,与实际问题无关)

    这意味着你的结构的用户可以选择'a任意,包括选择一些比你的结构的生命周期更长的生命周期,例如'static。现在观察这种选择如何改变结构(只需将'static 替换为'a):

    struct FooStatic<S: Send, T: Send> {
        next_: Box<Fn<(&'static S,), Option<T>> + Send>,  // '
        state: S
    }
    

    突然间,闭包只能接受 'static 引用,这显然不是你的情况 - next() 方法中 self 的生命周期可能更短,所以你不能只将它传递给闭包。这只有在 self 生命周期确实对应于 'a 时才有效(如编译器所建议的那样):

    fn next(&'a mut self) -> Option<T>
    

    但是,正如我之前所说,你不能这样写,因为它会违反 trait contract。

    对于更高种类的生命周期,可以在闭包本身上指定生命周期参数:

    struct Foo<S: Send, T: Send> {
        next_: Box<for<'a> Fn<(&'a mut S,), Option<T>> + Send>,
        state: S
    }
    

    这样闭包的生命周期参数由闭包的调用者选择,在这种情况下,它是Iterator trait的实现者(即你:)),因此可以调用next_任何引用,包括对Foo internals 的引用。

    【讨论】:

      【解决方案3】:

      您可以使用itertools 中的iterate 来满足您的需要。

      否则,如果您想将所有逻辑包含在闭包中而不使用 状态概念,您可以通过以下方式实现它:

      struct IterClosure<T, C>(C) where C: FnMut() -> Option<T>;
      
      impl<T, C> Iterator for IterClosure<T, C> where C: FnMut() -> Option<T>
      {
          type Item = T;
      
          fn next(&mut self) -> Option<Self::Item> {
              (self.0)()
          }
      }
      
      fn iter<T>(f: impl FnMut() -> Option<T>) -> impl Iterator<Item=T> {
          IterClosure(f)
      }
      
      fn main() {
          let mut it = (0..10).into_iter();
          let mut closure = || it.next();
          println!("{}", iter(closure).sum::<i32>());
      
          let mut it = (0..10).into_iter();
          iter(|| it.next()).for_each(
              |i| println!("{}", i)
          )
      }
      

      【讨论】:

        猜你喜欢
        • 2020-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多