繁体   English   中英

如何为简单的结构实现 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是一个特征。 特征由结构实现,它们本身并不存在。 你也可以有一个引用 trait 对象( &Iterator )、一个装箱的 trait 对象( Box<Iterator> )或一个匿名 trait 实现( 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);
    }
}

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


通过重用已经存在的迭代器来编写它会更方便,例如,使用[T; 3] [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返回std::slice::Iter struct

其次,典型的数组[1, 2, 3]不在堆上分配。 事实上,编译器可以完全优化分配,改为指向预编译的数组。 能够迭代数组而不将它们复制到任何地方是我认为数组的IntoIterator实现不会像其他IntoIterator实现那样将数组移动到任何地方的IntoIterator 相反,它似乎引用了现有数组。 它的签名中可以看出

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] )。

因此,您不能以您想要的方式使用它。 被引用的数组必须比返回的迭代器寿命更长。 这是Rust 编译器告诉我们的一个版本

Vector 有一个IntoIterator实现,它真正将数据移动到迭代器中,因此您可以使用它


PS 为了既快速又简单,返回一个数组而不是迭代器( 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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM