Ответ 1
Ваш тип итератора - 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
, вам также необходимо реализовать итератор, который его не использует:
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 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)
}
}