[英]How to implement Iterator and IntoIterator for a simple struct?
How would someone implement the Iterator
and IntoIterator
traits for the following struct?有人将如何为以下结构实现
Iterator
和IntoIterator
特征?
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`
Your iterator type is Iterator<Item = Self::Item>
, but Iterator
is a trait.您的迭代器类型是
Iterator<Item = Self::Item>
,但Iterator
是一个特征。 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.你也可以有一个引用 trait 对象(
&Iterator
)、一个装箱的 trait 对象( Box<Iterator>
)或一个匿名 trait 实现( impl Iterator
),所有这些都有一个已知的大小。
Instead, we create a PixelIntoIterator
that has a known size and implements Iterator
itself:相反,我们创建了一个
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);
}
}
This has the nice benefit of returning actual i8
s, not references.这具有返回实际
i8
而非引用的好处。 Since these are so small, you might as well pass them directly.由于这些很小,您不妨直接传递它们。
This consumes the Pixel
.这会消耗
Pixel
。 If you had a reference to a Pixel
, you'd need to also implement an iterator that doesn't consume it:如果您有对
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)
}
}
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.您始终可以引用您拥有的
Pixel
,因此您只需要非消耗变量。 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, eg, with
[T; 3]
通过重用已经存在的迭代器来编写它会更方便,例如,使用
[T; 3]
[T; 3]
As of Rust 1.51, you can leverage array::IntoIter
:从 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])
}
}
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
:在以前的版本中,这可能有点傻,但是您可以通过将一些现有类型粘合在一起并使用
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)
}
}
First, IntoIter
must point to a real struct
and not to a trait
in order for Rust to be able to pass the value around (that's what Sized
means).首先,
IntoIter
必须指向一个真正的struct
而不是一个trait
,以便 Rust 能够传递值(这就是Sized
意思)。 In case of arrays into_iter
returns the std::slice::Iter struct
.如果是数组
into_iter
返回std::slice::Iter struct
。
Second, a typical array, [1, 2, 3]
, isn't allocated on heap.其次,典型的数组
[1, 2, 3]
不在堆上分配。 In fact, the compiler is allowed to optimize away the allocation entirely, pointing to a pre-compiled array instead.事实上,编译器可以完全优化分配,改为指向预编译的数组。 Being able to iterate the arrays without copying them anywhere is I think the reason why the
IntoIterator
implementation for arrays doesn't move the array anywhere as other IntoIterator
implementations do.能够迭代数组而不将它们复制到任何地方是我认为数组的
IntoIterator
实现不会像其他IntoIterator
实现那样将数组移动到任何地方的IntoIterator
。 Instead it seems to reference the existing array.相反,它似乎引用了现有数组。 You can see from 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>
that it takes a reference to an array ( &'a [T; 3]
).它需要对数组的引用(
&'a [T; 3]
)。
As such, you can't use it in the way you're trying to.因此,您不能以您想要的方式使用它。 The referenced array must outlive the returned iterator.
被引用的数组必须比返回的迭代器寿命更长。 Here's a version where Rust compiler tells so.
这是Rust 编译器告诉我们的一个版本。
Vector has an IntoIterator
implementation that truly moves the data into the iterator and so you can use it . Vector 有一个
IntoIterator
实现,它真正将数据移动到迭代器中,因此您可以使用它。
PS To make it both fast and simple, return an array instead of an iterator ( playpen ): PS 为了既快速又简单,返回一个数组而不是迭代器( playpen ):
impl Pixel {
fn into_array(self) -> [i8; 3] {[self.r, self.g, self.b]}
}
That way the array is first moved into the outer scope and then it can be referenced from the outer scope's iterator:这样数组首先被移动到外部作用域,然后它可以被外部作用域的迭代器引用:
for color in &(Pixel {r: 1, g: 2, b: 3}).into_array() {
println! ("{}", color);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.