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`
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.
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.
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.
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.
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 i8
s, 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) } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With