首页 文章

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

提问于
浏览
38

如何为以下结构实现 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`

2 回答

  • 55

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

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

    这会消耗 Pixel . 如果你有一个 Pixel 的引用,你'd need to also implement an iterator that doesn' t消耗它:

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

    如果您想支持创建消费迭代器和非消耗迭代器,则可以实现这两个版本 . 您始终可以引用您拥有的 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)
        }
    }
    
  • 2

    首先, IntoIter 必须指向一个真正的 struct 而不是指向 trait ,以便Rust能够传递该值(这就是 Sized 的含义) . 如果数组 into_iter 返回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] ) .

    因此,你可以尝试.2859910 . 引用的数组必须比返回的迭代器更长 . Here's a version Rust编译器这么说 .

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


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

相关问题