我如何 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))
}
}
这给我以下错误:
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。然而,没有必要有两个生命周期界限:一个就足够了。如有必要,编译器将缩短此单一生命周期以满足 self
和 input_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
.
我在 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))
}
}
这给我以下错误:
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。然而,没有必要有两个生命周期界限:一个就足够了。如有必要,编译器将缩短此单一生命周期以满足 self
和 input_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
.