web-dev-qa-db-ja.com

単純な構造体にIteratorとIntoIteratorを実装する方法は?

次の構造体の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`
42
Piper Merriam

イテレータタイプは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);
    }
}

これには、参照ではなく、実際のi8sを返すという利点があります。これらは非常に小さいため、直接渡すこともできます。

これは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)
    }
}
67
Shepmaster

最初に、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);
}
3
ArtemGr