IntoIterator

Iterator トレイトは、イテレータを作成した後に反復処理を行う方法を示します。関連するトレイト IntoIterator は、ある型に対するイテレータを作成する方法を定義します。これは for ループによって自動的に使用されます。

struct Grid {
    x_coords: Vec<u32>,
    y_coords: Vec<u32>,
}

impl IntoIterator for Grid {
    type Item = (u32, u32);
    type IntoIter = GridIter;
    fn into_iter(self) -> GridIter {
        GridIter { grid: self, i: 0, j: 0 }
    }
}

struct GridIter {
    grid: Grid,
    i: usize,
    j: usize,
}

impl Iterator for GridIter {
    type Item = (u32, u32);

    fn next(&mut self) -> Option<(u32, u32)> {
        if self.i >= self.grid.x_coords.len() {
            self.i = 0;
            self.j += 1;
            if self.j >= self.grid.y_coords.len() {
                return None;
            }
        }
        let res = Some((self.grid.x_coords[self.i], self.grid.y_coords[self.j]));
        self.i += 1;
        res
    }
}

fn main() {
    let grid = Grid { x_coords: vec![3, 5, 7, 9], y_coords: vec![10, 20, 30, 40] };
    for (x, y) in grid {
        println!("point = {x}, {y}");
    }
}
This slide should take about 5 minutes.

IntoIteratorのドキュメントをクリックしてご覧ください。IntoIterator のすべての実装で、次の 2 つの型を宣言する必要があります。

  • Item: 反復処理する型(i8 など)。
  • IntoIter: into_iter メソッドによって返される Iterator 型。

IntoIterItemは関連があり、イテレータはItemと同じ型を持つ必要があります。すなわち、Option<Item>を返します。

この例は、x 座標と y 座標のすべての組み合わせを反復処理しています。

main でグリッドを 2 回反復処理してみましょう。これはなぜ失敗するのでしょうか。IntoIterator::into_iterself の所有権を取得することに着目してください。

この問題を修正するには、&GridIntoIterator を実装し、Grid への参照を GridIter に保存します。

標準ライブラリ型でも同じ問題が発生する可能性があります。for e in some_vector は、some_vector の所有権を取得し、そのベクターの所有要素を反復処理します。some_vector の要素への参照を反復処理するには、代わりに for e in &some_vector を使用します。