实现' IntoIterator ',将数组转换为结构体



我有一个基本的结构体ImageData,它主要保存一个表示PNG图像的u8s (bytes)数组(布局为rgbrbrgb…或rgbargbargba…):

struct ImageData {
png_type: png::ColorType,
bytes: Vec<u8>,
width: u32,
height: u32,
is_srgb: bool,
}

我也有一个PixelValue结构体,这将允许我类似地对待RGB和RGBA -也就是说,我想得到PixelValues而不是3或4个u8s的数组,因为这样我就会复制很多代码来处理那些不同的类型。

struct PixelValue {
r: u8,
g: u8,
b: u8,
a: u8,
}

所以我想在ImageData上实现IntoIterator,它将在PixelValue上返回一个迭代器。它应该将u8块转换为PixelValues:

impl<'a> IntoIterator for &'a ImageData {
type Item = PixelValue;
type IntoIter = Iter<PixelValue>; // this is definitely wrong
fn into_iter(self) -> Self::IntoIter {
let pixel_size = get_pixel_size(self.png_type);  // 3 or 4, depending on RGB or RGBA
// Need to write something that can take an array of 3 or 4 u8s and returns the PixelValue
self.bytes.chunks(pixel_size).map(|ar| PixelValue(ar))
}
}

我的问题是:

  • 我走的方向对吗?我可以用map改变迭代器的输出值(Item)吗?如果不是,为什么不呢?
  • 是否有明显的"最佳实践"?如何解决这个问题(将像素RGB或rgba的数组转换为结构体)?

您可以使用动态分派,将迭代器包装在Box中:

struct ImageData {
png_type: u8,
bytes: Vec<u8>,
width: u32,
height: u32,
is_srgb: bool,
}
struct PixelValue {
r: u8,
g: u8,
b: u8,
a: u8,
}
impl<'a> IntoIterator for &'a ImageData {
type Item = PixelValue;
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
fn into_iter(self) -> Self::IntoIter {
let size = 3;
Box::new(self.bytes.chunks(size).map(move |chunk| match size {
3 => PixelValue {
r: chunk[0],
g: chunk[1],
b: chunk[2],
a: 255,
},
_ => PixelValue {
r: chunk[0],
g: chunk[1],
b: chunk[2],
a: chunk[3],
},
}))
}
}

游乐场

请注意,在这个例子中,我更改了一些我没有访问权限的类型(png_typeget_size方法)。

您可以创建实现IteratorImageDataIntoPixelValueIterator,并将其用作type IntoIter。注:我已经从ImageData中删除了png_type,以便使此代码可重现,并假装alpha通道始终存在:

struct ImageData {
bytes: Vec<u8>,
width: u32,
height: u32,
is_srgb: bool,
}
struct PixelValue {
r: u8,
g: u8,
b: u8,
a: u8,
}
impl IntoIterator for ImageData {
type Item = PixelValue;
type IntoIter = ImageDataIntoPixelValueIterator;
fn into_iter(self) -> Self::IntoIter {
ImageDataIntoPixelValueIterator {
bytes: self.bytes,
index: 0,
}
}
}
struct ImageDataIntoPixelValueIterator {
bytes: Vec<u8>,
index: usize,
}
impl Iterator for ImageDataIntoPixelValueIterator {
type Item = PixelValue;
fn next(&mut self) -> Option<PixelValue> {
// assumes that self.bytes.len() is a multiple of four.
// this should probably be asserted somewhere else
if self.index >= self.bytes.len() {
None
} else {
let res = PixelValue {
r: self.bytes[self.index],
g: self.bytes[self.index + 1],
b: self.bytes[self.index + 2],
a: self.bytes[self.index + 3],
};
self.index += 4;
Some(res)
}
}
}

游乐场

我已经根据你的具体需求基本实现了这个答案。请仔细阅读,因为它提供了丰富的解释性信息,帮助你理解为什么某些事情会或不会工作。

最新更新