如何为简单结构实现 Iterator 和 IntoIterator?

How to implement Iterator and IntoIterator for a simple struct?

如何为以下结构实现 IteratorIntoIterator 特征?

struct Pixel {
    r: i8,
    g: i8,
    b: i8,
}

我已经尝试了以下各种形式但没有成功。

impl IntoIterator for Pixel {
    type Item = i8;
    type IntoIter = Iterator<Item=Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        [&self.r, &self.b, &self.g].into_iter()
    }
}

此代码出现编译错误

error[E0277]: the trait bound `std::iter::Iterator<Item=i8> + 'static: std::marker::Sized` is not satisfied
 --> src/main.rs:7:6
  |
7 | impl IntoIterator for Pixel {
  |      ^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `std::iter::Iterator<Item=i8> + 'static`
  |
  = note: `std::iter::Iterator<Item=i8> + 'static` does not have a constant size known at compile-time
  = note: required by `std::iter::IntoIterator`

您的迭代器类型是 Iterator<Item = Self::Item>,但 Iterator 是一个特征。特征由结构实现,它们本身并不存在。您还可以有一个参考特征对象(&Iterator)、一个盒装特征对象(Box<Iterator>)或一个匿名特征实现(impl Iterator),所有这些都有一个已知的大小。

相反,我们创建了一个已知大小的 PixelIntoIterator 并且 实现了 Iterator 本身:

struct Pixel {
    r: i8,
    g: i8,
    b: i8,
}

impl IntoIterator for Pixel {
    type Item = i8;
    type IntoIter = PixelIntoIterator;

    fn into_iter(self) -> Self::IntoIter {
        PixelIntoIterator {
            pixel: self,
            index: 0,
        }
    }
}

pub struct PixelIntoIterator {
    pixel: Pixel,
    index: usize,
}

impl Iterator for PixelIntoIterator {
    type Item = i8;
    fn next(&mut self) -> Option<i8> {
        let result = match self.index {
            0 => self.pixel.r,
            1 => self.pixel.g,
            2 => self.pixel.b,
            _ => return None,
        };
        self.index += 1;
        Some(result)
    }
}

fn main() {
    let p = Pixel {
        r: 54,
        g: 23,
        b: 74,
    };
    for component in p {
        println!("{}", component);
    }
}

这具有 returning 实际 i8 的好处,而不是引用。既然这么小,你还不如直接传过去。

这会消耗 Pixel。如果您有对 Pixel 的引用,您还需要实现一个不使用它的迭代器:

impl<'a> IntoIterator for &'a Pixel {
    type Item = i8;
    type IntoIter = PixelIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        PixelIterator {
            pixel: self,
            index: 0,
        }
    }
}

pub struct PixelIterator<'a> {
    pixel: &'a Pixel,
    index: usize,
}

impl<'a> Iterator for PixelIterator<'a> {
    type Item = i8;
    fn next(&mut self) -> Option<i8> {
        let result = match self.index {
            0 => self.pixel.r,
            1 => self.pixel.g,
            2 => self.pixel.b,
            _ => return None,
        };
        self.index += 1;
        Some(result)
    }
}

如果您想支持创建消费迭代器和非消费迭代器,您可以实现这两个版本。您始终可以引用您拥有的 Pixel,因此您只 需要 非消耗变体。但是,拥有一个消费版本通常很好,这样您就可以 return 迭代器而不必担心生命周期。


it'd be much more convenient to write this by reusing iterators that already exists, e.g., with [T; 3]

从 Rust 1.51 开始,您可以利用 array::IntoIter:

impl IntoIterator for Pixel {
    type Item = i8;
    type IntoIter = std::array::IntoIter<i8, 3>;

    fn into_iter(self) -> Self::IntoIter {
        std::array::IntoIter::new([self.r, self.b, self.g])
    }
}

在以前的版本中,这可能有点傻,但是您可以通过将一些现有类型粘合在一起并使用 impl Iterator:

来避免创建自己的迭代器类型
use std::iter;

impl Pixel {
    fn values(&self) -> impl Iterator<Item = i8> {
        let r = iter::once(self.r);
        let b = iter::once(self.b);
        let g = iter::once(self.g);
        r.chain(b).chain(g)
    }
}

首先,IntoIter 必须指向一个真正的 struct 而不是 trait 以便 Rust 能够传递值(这就是 Sized 方法)。对于数组 into_iter returns std::slice::Iter struct.

其次,典型的数组 [1, 2, 3] 没有分配在堆上。事实上,允许编译器完全优化分配,而是指向一个预编译的数组。能够迭代数组而不将它们复制到任何地方是我认为数组的 IntoIterator 实现不会像其他 IntoIterator 实现那样 移动 数组的原因做。相反,它似乎 引用 现有数组。从its signature

可以看出
impl<'a, T> IntoIterator for &'a [T; 3]
    type Item = &'a T
    type IntoIter = Iter<'a, T>
    fn into_iter(self) -> Iter<'a, T>

需要对数组 (&'a [T; 3]) 的 引用

因此,您不能按照您想要的方式使用它。引用的数组必须比 returned 迭代器长。 Here's a version Rust 编译器告诉的地方。

Vector 有一个 IntoIterator 实现,它真正将数据移动到迭代器中,因此 you can use it


P.S。为了使它既快速又简单,return 一个数组而不是迭代器 (playpen):

impl Pixel {
    fn into_array(self) -> [i8; 3] {[self.r, self.g, self.b]}
}

这样数组首先被移动到外部作用域,然后它可以被外部作用域的迭代器引用

for color in &(Pixel {r: 1, g: 2, b: 3}).into_array() {
    println! ("{}", color);
}