如何为简单结构实现 Iterator 和 IntoIterator?
How to implement Iterator and IntoIterator for a simple struct?
如何为以下结构实现 Iterator
和 IntoIterator
特征?
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`
您的迭代器类型是 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,
}
}
}
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);
}
}
这具有 returning 实际 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,
}
}
}
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)
}
}
如果您想支持创建消费迭代器和非消费迭代器,您可以实现这两个版本。您始终可以引用您拥有的 Pixel
,因此您只 需要 非消耗变体。但是,拥有一个消费版本通常很好,这样您就可以 return 迭代器而不必担心生命周期。
it'd be much more convenient to write this by reusing iterators that already exists, e.g., with [T; 3]
从 Rust 1.51 开始,您可以利用 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])
}
}
在以前的版本中,这可能有点傻,但是您可以通过将一些现有类型粘合在一起并使用 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)
}
}
首先,IntoIter
必须指向一个真正的 struct
而不是 trait
以便 Rust 能够传递值(这就是 Sized
方法)。对于数组 into_iter
returns 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]
) 的 引用。
因此,您不能按照您想要的方式使用它。引用的数组必须比 returned 迭代器长。 Here's a version Rust 编译器告诉的地方。
Vector 有一个 IntoIterator
实现,它真正将数据移动到迭代器中,因此 you can use it。
P.S。为了使它既快速又简单,return 一个数组而不是迭代器 (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);
}
如何为以下结构实现 Iterator
和 IntoIterator
特征?
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`
您的迭代器类型是 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,
}
}
}
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);
}
}
这具有 returning 实际 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,
}
}
}
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)
}
}
如果您想支持创建消费迭代器和非消费迭代器,您可以实现这两个版本。您始终可以引用您拥有的 Pixel
,因此您只 需要 非消耗变体。但是,拥有一个消费版本通常很好,这样您就可以 return 迭代器而不必担心生命周期。
it'd be much more convenient to write this by reusing iterators that already exists, e.g., with
[T; 3]
从 Rust 1.51 开始,您可以利用 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])
}
}
在以前的版本中,这可能有点傻,但是您可以通过将一些现有类型粘合在一起并使用 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)
}
}
首先,IntoIter
必须指向一个真正的 struct
而不是 trait
以便 Rust 能够传递值(这就是 Sized
方法)。对于数组 into_iter
returns 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]
) 的 引用。
因此,您不能按照您想要的方式使用它。引用的数组必须比 returned 迭代器长。 Here's a version Rust 编译器告诉的地方。
Vector 有一个 IntoIterator
实现,它真正将数据移动到迭代器中,因此 you can use it。
P.S。为了使它既快速又简单,return 一个数组而不是迭代器 (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);
}