Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Iterator and IntoIterator for a simple struct?

Tags:

iterator

rust

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` 
like image 231
Piper Merriam Avatar asked May 13 '15 15:05

Piper Merriam


People also ask

Can iterator be implemented as an inner class?

Note: The Iterator class can also, be implemented as an inner class of the Data Structure class since it won’t be used elsewhere. How next () and hasNext () work? To implement an Iterator, we need a cursor or pointer to keep track of which element we currently are on.

How to pass an object of a data structure to iterator?

If the Iterator class is implemented as a separate class, we can pass this object of the data structure to the iterator class constructor as demonstrated in the example below. Given below is a Custom Linked List which makes use of Generics.

How to implement an iterable data structure in Java?

To implement an iterable data structure, we need to: Create an Iterator class which implements Iterator interface and corresponding methods. We can generalize the pseudo code as follows: // Used to remove an element. Implement only if needed // Default throws UnsupportedOperationException.

Why do we need to iterate through a data structure?

This way we don’t invalidate the iterator and we can move on with the traversal. Building an iterator will simplify the code for the given data structure just because the iterator is responsible for traversing the structure and not the structure itself, the structure’s only responsibility is to supply the start and the end for the iterator.


1 Answers

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.

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

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

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

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.


it'd be much more convenient to write this by reusing iterators that already exists, e.g., with [T; 3]

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

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)     } } 
like image 57
Shepmaster Avatar answered Sep 20 '22 12:09

Shepmaster