我如何 return 具有多个生命周期的 impl Iterator?

How can I return an impl Iterator that has multiple lifetimes?

我在 impl 特征的生命周期方面遇到了问题。我正在尝试让以下代码工作:

struct Foo<'op, Input> {
    op: Box<dyn Fn(Input) -> i32 + 'op>,
}

impl<'op, Input> Foo<'op, Input> {
    fn new<Op>(op: Op) -> Foo<'op, Input>
    where
        Op: Fn(Input) -> i32 + 'op,
    {
        Foo { op: Box::new(op) }
    }

    fn apply<'input_iter, InputIter>(
        self,
        input_iter: InputIter,
    ) -> impl Iterator<Item = i32> + 'op + 'input_iter
    where
        InputIter: IntoIterator<Item = Input> + 'input_iter,
    {
        input_iter.into_iter().map(move |input| (self.op)(input))
    }
}

(Playground)

这给我以下错误:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/lib.rs:20:36
   |
20 |         input_iter.into_iter().map(move |input| (self.op)(input))
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime 'op as defined on the impl at 5:6...
  --> src/lib.rs:5:6
   |
5  | impl<'op, Input> Foo<'op, Input> {
   |      ^^^
   = note: ...so that the types are compatible:
           expected Foo<'_, _>
              found Foo<'op, _>
note: but, the lifetime must be valid for the lifetime 'input_iter as defined on the method body at 13:14...
  --> src/lib.rs:13:14
   |
13 |     fn apply<'input_iter, InputIter>(
   |              ^^^^^^^^^^^
note: ...so that return value is valid for the call
  --> src/lib.rs:16:10
   |
16 |     ) -> impl Iterator<Item = i32> + 'op + 'input_iter
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

这是我对涉及的生世的理解。 Foo 拥有一个 op,它是一个可能在某处有引用的闭包,因此它可能有一个有限的生命周期。这由 'op 表示,并且 Foo 受到限制,因此它不能比它更长寿。到目前为止,还不错。

在 apply() 中,我们想要使用 input_iter 和 self 以及 return 中使用 self.op 映射的每个元素的迭代器 input_iter . input_iterator 也可能包含引用,因此它可能有自己的生命周期边界,用 'input_iter.

表示

我想要的是 return 一个同时拥有 self 和 input_iter 所有权的迭代器。在这样做时,它必须接受它们的两个生命周期参数,以确保它不会比 input_iter 引用或 op 引用更持久。我以为 impl Iterator<Item = i32> + 'op + 'input_iter 可以做到这一点,但我似乎在某个地方走错了路。

它抱怨关闭也很奇怪。我知道闭包不能比 'op 长寿,因为它拥有运算符及其引用的所有权。这是完全有道理的。我不明白的是为什么它需要和'input_iter一样长。闭包和输入迭代器根本不应该关心彼此;唯一连接它们的是它们都有相同的所有者(输出迭代器)。

我在这里错过了什么?

生命周期参数并不总是代表对象(或借用)的准确生命周期。让我们考虑这个例子:

fn main() {
    let a = 3;
    let b = 5;
    let c = min(&a, &b);
    println!("{:?}", c);
}

fn min<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
    if a < b {
        a
    } else {
        b
    }
}

这里,min 接受两个具有相同生命周期的引用参数。但是,我们通过借用具有不同生命周期的不同变量来调用它。那么为什么这段代码可以编译?

答案是subtyping and variance。更长的生命周期是更短生命周期的 亚型 ;相反,较短的生命周期是较长生命周期的超类型。在上面的示例中,编译器必须找到一个与两个输入生命周期兼容的生命周期。编译器通过找到两个生命周期的公共超类型(即包含两者的最短生命周期)来做到这一点。这就叫统一.


回到你的问题。目前看来编译器 doesn't handle multiple lifetime bounds on impl Trait too well。然而,没有必要有两个生命周期界限:一个就足够了。如有必要,编译器将缩短此单一生命周期以满足 selfinput_iter.

的要求
struct Foo<'op, Input> {
    op: Box<dyn Fn(Input) -> i32 + 'op>,
}

impl<'op, Input> Foo<'op, Input> {
    fn new<Op>(op: Op) -> Foo<'op, Input>
    where
        Op: Fn(Input) -> i32 + 'op,
    {
        Foo { op: Box::new(op) }
    }

    fn apply<InputIter>(
        self,
        input_iter: InputIter,
    ) -> impl Iterator<Item = i32> + 'op
    where
        Input: 'op,
        InputIter: IntoIterator<Item = Input> + 'op,
    {
        input_iter.into_iter().map(move |input| (self.op)(input))
    }
}

fn main() {
    let y = 1;
    let foo = Foo::new(|x| x as i32 + y);

    let s = "abc".to_string();
    let sr: &str = &*s;
    let bar = sr.chars();

    let baz: Vec<_> = foo.apply(bar).collect();
    println!("{:?}", baz);
}

尝试移动 main 中的线条以说服自己生命周期确实是统一的。

在这一点上,调用生命周期'op有点误导;它也可以称为 'a.