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

Pip*_*iam 43 iterator rust

有人会如何实现以下结构的特征IteratorIntoIterator特征?

struct Pixel {
    r: i8,
    g: i8,
    b: i8,
}
Run Code Online (Sandbox Code Playgroud)

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

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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`
Run Code Online (Sandbox Code Playgroud)

She*_*ter 69

你的迭代器类型是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,
        }
    }
}

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

返回实际的i8s而不是引用有很好的好处.由于它们很小,你可以直接传递它们.

这消耗了Pixel.如果你有一个对a的引用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,
        }
    }
}

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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


它可能有点傻,但您可以通过将一些现有类型粘合在一起并使用impl trait来避免创建自己的迭代器类型:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)


Art*_*mGr 9

首先,IntoIter必须指向实数struct而不是 a trait,以便 Rust 能够传递值(这就是意思Sized)。如果是数组,into_iter则返回std::slice::Iter struct

其次,典型的数组[1, 2, 3]不是在堆上分配的。事实上,编译器可以完全优化分配,而是指向预编译的数组。我认为能够迭代数组而不将它们复制到任何地方是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>
Run Code Online (Sandbox Code Playgroud)

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

因此,您无法按照您想要的方式使用它。引用的数组必须比返回的迭代器存在得更久。这是Rust 编译器告诉我们的版本。

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


PS 为了使其既快速又简单,请返回一个数组而不是迭代器(playpen):

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

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

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

  • 使用 `vec!` 添加堆分配,它比 Shepmaster 的版本慢。在我的机器上,Shepmaster 的版本 (http://is.gd/BJUSbZ) 的执行速度为 **1 ns/iter**。`vec!` 版本 (http://is.gd/CMNqzR) 的执行速度为 **23 ns/iter**。返回数组的版本(http://is.gd/pr6Zaf)既快速又易于实现,它在我的硬件上的执行速度为**1 ns/iter**。 (3认同)