迭代器返回引用的项,生命周期问题

12

我有一个生命周期问题,我正在尝试实现一个通过引用返回其项的迭代器,以下是代码:

struct Foo {
   d: [u8; 42],
   pos: usize
}

impl<'a> Iterator<&'a u8> for Foo {
   fn next<'a>(&'a mut self) -> Option<&'a u8> {
      let r = self.d.get(self.pos);
      if r.is_some() {
         self.pos += 1;
      }
      r
   }
}

fn main() {
   let mut x = Foo {
      d: [1; 42],
      pos: 0
   };

   for i in x {
      println!("{}", i);
   }
}

然而,这段代码无法正确编译,出现了与参数生命周期相关的问题,以下是相应的错误:

$ rustc test.rs
test.rs:8:5: 14:6 error: method `next` has an incompatible type for trait: expected concrete lifetime, but found bound lifetime parameter
test.rs:8     fn next<'a>(&'a mut self) -> Option<&'a u8> {
test.rs:9         let r = self.d.get(self.pos);
test.rs:10         if r.is_some() {
test.rs:11             self.pos += 1;
test.rs:12         }
test.rs:13         r
           ...
test.rs:8:49: 14:6 note: expected concrete lifetime is the lifetime 'a as defined on the block at 8:48
test.rs:8     fn next<'a>(&'a mut self) -> Option<&'a u8> {
test.rs:9         let r = self.d.get(self.pos);
test.rs:10         if r.is_some() {
test.rs:11             self.pos += 1;
test.rs:12         }
test.rs:13         r
           ...
error: aborting due to previous error

有没有人有解决这个问题并仍然返回项的引用的想法?

至少这条消息是什么意思:期望具体的生命周期,但发现绑定的生命周期参数

1个回答

17

Note on the version of Rust used: at the time this question and answer were written, the Iterator trait used generics; it has changed to use associated types and is now defined thus:

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    …
}

And so the incorrect implementation shown here would be like this:

impl<'a> Iterator for Foo {
    type Item = &'a u8;

    fn next<'a>(&'a mut self) -> Option<&'a u8>;
}

In practical terms this affects nothing; it is merely that A becomes Self::Item.

迭代器(Iterator)特质的定义如下:
pub trait Iterator<A> {
    fn next(&mut self) -> Option<A>;
    …
}

请注意:fn next(&mut self) -> Option<A>

下面是你拥有的:

impl<'a> Iterator<&'a u8> for Foo {
    fn next<'a>(&'a mut self) -> Option<&'a u8>;
}

请注意:fn next<'a>(&'a mut self) -> Option<&'a u8>
这里存在几个问题:
1. 你引入了一个新的泛型参数<'a>,但实际上它不应该存在。为了方便起见并强调发生的情况,我将在impl块中定义的'a称为ρ₀,并将在方法中定义的'a称为ρ₁。它们不相同。 2. &mut self的生命周期与特征不同。
3. 返回类型的生命周期与特征不同:其中A&'ρ₀ u8,而返回类型在A的位置上使用&'ρ₁ u8。它期望具体的生命周期ρ₀,但实际上找到的是生命周期ρ₁。(我不确定“bound”是什么意思,所以我会保持沉默,以免我错了。)
这就是问题所在:你不能将要迭代的对象的生命周期连接到&mut self。相反,它必须绑定到你正在为其实现特征的类型中的某个东西。例如,通过创建连接到基本切片的新迭代器对象来迭代切片中的项目,impl<'a, T> Iterator<&'a T> for Items<'a, T>。换句话说,如果你正在生成引用,则迭代特征的设计方式不是为了在self中返回某些东西,而是为了在你拥有引用的另一个对象内部返回某些东西。
对于你的特定、可能是简单的例子,你应该停止产生引用,或者改变它,使你的迭代器对象不包含你正在迭代的数据,让它仅仅包含对它的引用,例如:&'a [T] 或者类似Items<'a, T>的东西。

非常有帮助的回答。我必须说:1- 在理解和正确使用在struct和traits中指定的lifetime/generic types/traits types以及在方法中使用的这些类型方面,我正在努力学习,因此在示例中到处使用“a”的错误使用。 2- 我认为我很好地理解了你的观点,我尝试在Rust代码及其库中查找类似情况是如何处理的,并且你所说的似乎对于在struct中使用生存期参数并具有引用属性的情况(例如libcore/slice.rs中的Splits迭代器)相当一致。感谢您的帮助。 - user3762625
有时候你可能会发现标准库中的示例很难理解;例如,Items实际上是使用宏生成的,因此在你能够理解它之前,你需要了解Rust宏的基础知识!随时准备好访问irc://irc.mozilla.org/#rust,那里总有人可以提供帮助。 - Chris Morgan
1
我想指出一个重要的原因,即为什么要返回一个生命周期与迭代器无关的项:这允许在迭代中实现转换,而这在C++中非常难以实现。 - Matthieu M.
这个答案已经不再适用了。显然,2015年合并的RFC447破坏了它,现在我会得到错误impl<'a> Iterator for Foo { // unconstrained lifetime parameter - Jonas Berlin
@JonasBerlin:这个答案从来没有包含可运行的代码,它只是解释了为什么尝试的东西不起作用。 - Chris Morgan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接