首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何为一个简单的结构实现迭代器和IntoIterator?

如何为一个简单的结构实现迭代器和IntoIterator?
EN

Stack Overflow用户
提问于 2015-05-13 23:11:33
回答 2查看 37K关注 0票数 79

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

代码语言:javascript
复制
struct Pixel {
    r: i8,
    g: i8,
    b: i8,
}

我已经尝试了下面的各种形式,但都没有成功。

代码语言:javascript
复制
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()
    }
}

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

代码语言:javascript
复制
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`
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-05-14 00:47:34

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

取而代之的是,我们创建一个已知大小并实现Iterator本身的PixelIntoIterator

代码语言:javascript
复制
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的引用,你还需要实现一个不使用它的迭代器:

代码语言:javascript
复制
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] )来编写

要方便得多

从Rust1.51开始,您可以利用array::IntoIter

代码语言:javascript
复制
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来避免创建自己的迭代器类型

代码语言:javascript
复制
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)
    }
}
票数 123
EN

Stack Overflow用户

发布于 2015-05-14 00:59:45

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

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

代码语言:javascript
复制
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编译器告诉我们的Here's a version

Vector有一个IntoIterator实现,可以真正地将数据移动到迭代器中,从而实现you can use it

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

代码语言:javascript
复制
impl Pixel {
    fn into_array(self) -> [i8; 3] {[self.r, self.g, self.b]}
}

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

代码语言:javascript
复制
for color in &(Pixel {r: 1, g: 2, b: 3}).into_array() {
    println! ("{}", color);
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30218886

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档