次の構造体の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
)を使用することもできます。これらはすべて既知のサイズです。
代わりに、既知のサイズとimplementsPixelIntoIterator
自体を持つ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
sを返すという利点があります。これらは非常に小さいため、直接渡すこともできます。
これは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
への参照をいつでも取得できるため、need非消費バリアントのみです。ただし、ライフタイムを気にせずにイテレータを返すことができるように、多くの場合、消費バージョンを用意するのが良いでしょう。
少しばかげているかもしれませんが、既存の型をいくつか貼り付けて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
は、Rustが値を渡すことができるようにするために、struct
ではなく、実際のtrait
を指す必要があります(それがSized
の意味です。)配列の場合into_iter
は、 std :: slice :: Iterstruct
を返します。
第二に、典型的な配列、[1, 2, 3]
、ヒープには割り当てられません。実際、コンパイラーは割り当てを完全に最適化して、代わりに事前にコンパイルされた配列を指し示すことができます。どこにもコピーせずに配列を反復できるのは、配列のIntoIterator
実装が配列を移動しないmove理由です他のIntoIterator
実装のようにどこでも。代わりに、既存の配列をreferenceするようです。 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>
配列へのreferenceを取る(&'a [T; 3]
)。
そのため、あなたがしようとしている方法でそれを使用することはできません。参照された配列は、返されたイテレータよりも長持ちする必要があります。 ここにバージョンがあります ここでRustコンパイラーはそう伝えます。
Vectorには、データをイテレータに実際に移動するIntoIterator
実装があるため、 使用できます です。
追伸高速かつ単純にするために、イテレータではなく配列を返します( playpen ):
impl Pixel {
fn into_array(self) -> [i8; 3] {[self.r, self.g, self.b]}
}
そのようにして、配列は最初に外側のスコープに移動され、それからreferenced外側のスコープの反復子から:
for color in &(Pixel {r: 1, g: 2, b: 3}).into_array() {
println! ("{}", color);
}