问题 迭代器通过引用返回项目,终身问题


我有一个 一生 问题,我正在尝试实现一个迭代器,通过引用返回它的项目,这里是代码:

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

有人知道如何解决这个问题并仍然通过引用返回项目?

至少这个消息是什么意思: 预期的具体生命周期,但找到了生命周期参数 ?


6094
2017-07-04 12:48


起源



答案:


关于使用的Rust版本的注意事项: 当时这个问题和答案都是写的 Iterator 特质使用泛型;它已更改为使用关联类型,现在定义如下:

pub trait Iterator {
    type Item;

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

所以这里显示的错误实现将是这样的:

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

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

实际上,这没有任何影响;它仅仅是那个 A 变 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> 哪个不应该在那里。为了方便和强调这里发生的事情,我将配音 'a 定义在impl块ρ0和 'a 在方法ρ1上定义。 他们不一样。

  2. 一生的 &mut self 与特质不同。

  3. 返回类型的生命周期与特征不同:where A 是 &'ρ₀ u8,返回类型用于代替 A  &'ρ₁ u8。它期望混凝土寿命ρ0,但发现寿命ρ1。 (我不确定 恰恰 “绑定”位意味着什么,所以我会保持安静,以免我错了。)

以下是这相当于:您无法连接正在迭代的对象的生命周期 &mut self。相反,它必须绑定到您正在实现特征的类型中的某些内容。举个例子,通过创建连接到基础切片的新迭代器对象来迭代切片中的项目, impl<'a, T> Iterator<&'a T> for Items<'a, T>。以另一种方式表达,迭代特征的设计方式不是,如果你正在生成引用,你可以返回内部的东西 self,而是返回你引用的另一个对象内的东西。

对于特定的,可能是简单的示例,您应该停止产生引用,或者更改它以使您的迭代器对象不包含您正在迭代的数据 - 让它仅包含一个 参考 对它,例如 &'a [T] 甚至是类似的东西 Items<'a, T>


14
2017-07-04 13:35



很有帮助的回答。我必须说1-我正在努力理解并正确使用结构和特征中指定的生命/泛型类型/特征类型以及方法中使用的类型,因此我在这个例子中无处不在。 2-我想我很清楚你的观点,我试图grep Rust代码及其库,以便找到一个类似的案例是如何处理的,你所说的似乎对应于在struct中使用了lifetime参数的几种情况带有引用属性,例如libcore / slice.rs中Splits的迭代器。谢谢你的帮助 - user3762625
有时您可能会发现标准库中的示例难以理解;例如, Items 实际上是用宏生成的东西,所以你需要先了解Rust的宏的基础知识,然后才能理解它!随时准备好通过irc://irc.mozilla.org/#rust放下,总有人在那里提供帮助。 - Chris Morgan
我会注意到返回一个生命周期的项目有一个重要原因 不 链接到迭代器:这允许实现 转换 在迭代中,例如在C ++中很难做到的事情。 - Matthieu M.


答案:


关于使用的Rust版本的注意事项: 当时这个问题和答案都是写的 Iterator 特质使用泛型;它已更改为使用关联类型,现在定义如下:

pub trait Iterator {
    type Item;

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

所以这里显示的错误实现将是这样的:

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

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

实际上,这没有任何影响;它仅仅是那个 A 变 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> 哪个不应该在那里。为了方便和强调这里发生的事情,我将配音 'a 定义在impl块ρ0和 'a 在方法ρ1上定义。 他们不一样。

  2. 一生的 &mut self 与特质不同。

  3. 返回类型的生命周期与特征不同:where A 是 &'ρ₀ u8,返回类型用于代替 A  &'ρ₁ u8。它期望混凝土寿命ρ0,但发现寿命ρ1。 (我不确定 恰恰 “绑定”位意味着什么,所以我会保持安静,以免我错了。)

以下是这相当于:您无法连接正在迭代的对象的生命周期 &mut self。相反,它必须绑定到您正在实现特征的类型中的某些内容。举个例子,通过创建连接到基础切片的新迭代器对象来迭代切片中的项目, impl<'a, T> Iterator<&'a T> for Items<'a, T>。以另一种方式表达,迭代特征的设计方式不是,如果你正在生成引用,你可以返回内部的东西 self,而是返回你引用的另一个对象内的东西。

对于特定的,可能是简单的示例,您应该停止产生引用,或者更改它以使您的迭代器对象不包含您正在迭代的数据 - 让它仅包含一个 参考 对它,例如 &'a [T] 甚至是类似的东西 Items<'a, T>


14
2017-07-04 13:35



很有帮助的回答。我必须说1-我正在努力理解并正确使用结构和特征中指定的生命/泛型类型/特征类型以及方法中使用的类型,因此我在这个例子中无处不在。 2-我想我很清楚你的观点,我试图grep Rust代码及其库,以便找到一个类似的案例是如何处理的,你所说的似乎对应于在struct中使用了lifetime参数的几种情况带有引用属性,例如libcore / slice.rs中Splits的迭代器。谢谢你的帮助 - user3762625
有时您可能会发现标准库中的示例难以理解;例如, Items 实际上是用宏生成的东西,所以你需要先了解Rust的宏的基础知识,然后才能理解它!随时准备好通过irc://irc.mozilla.org/#rust放下,总有人在那里提供帮助。 - Chris Morgan
我会注意到返回一个生命周期的项目有一个重要原因 不 链接到迭代器:这允许实现 转换 在迭代中,例如在C ++中很难做到的事情。 - Matthieu M.