如何为简单的结构实现 Iterator 和 IntoIterator? [英] How to implement Iterator and IntoIterator for a simple struct?

查看:21
本文介绍了如何为简单的结构实现 Iterator 和 IntoIterator?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How would someone implement the Iterator and IntoIterator traits for the following struct?

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

我尝试了以下各种形式,但都没有成功.

I've tried various forms of the following with no success.

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()
    }
}

这段代码给了我一个编译错误

This code gives me a compile error

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,但是 Iterator 是一个 trait.特征由结构实现,它们本身并不存在.你也可以有一个引用 trait 对象(&Iterator)、一个装箱的 trait 对象(Box)或一个匿名的 trait 实现(impl Iterator),所有这些都有一个已知的大小.

Your iterator type is Iterator<Item = Self::Item>, but Iterator is a trait. Traits are implemented by structs, they don't exist on their own. You could also have a reference trait object (&Iterator), a boxed trait object (Box<Iterator>) or an anonymous trait implementation (impl Iterator), all of which have a known sizes.

相反,我们创建了一个PixelIntoIterator,它具有已知的大小和实现 Iterator 本身:

Instead, we create a PixelIntoIterator that has a known size and implements Iterator itself:

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);
    }
}

这具有返回实际 i8 而非引用的好处.既然这些都这么小,你还是直接传吧.

This has the nice benefit of returning actual i8s, not references. Since these are so small, you might as well pass them directly.

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

This consumes the Pixel. If you had a reference to a Pixel, you'd need to also implement an iterator that doesn't consume it:

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,因此您只需要 非消费变体.但是,拥有一个消费版本通常很好,这样您就可以返回迭代器而不必担心生命周期.

If you wanted to support creating both a consuming iterator and a non-consuming iterator, you can implement both versions. You can always take a reference to a Pixel you own, so you only need the non-consuming variant. However, it's often nice to have a consuming version so that you can return the iterator without worrying about lifetimes.

通过重用已经存在的迭代器来编写它会更方便,例如,使用 [T;3]

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:

As of Rust 1.51, you can leverage 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 来避免创建自己的迭代器类型:

In previous versions, it might be a bit silly, but you could avoid creating your own iterator type by gluing some existing types together and using 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)
    }
}

这篇关于如何为简单的结构实现 Iterator 和 IntoIterator?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆