IntoIterator

Iterator トレイトは、イテレータを作成したあとでどのように イテレートするか を示します。関連するトレイト IntoIterator は、ある型に対するイテレータの作成方法を定義します。これは for ループで自動的に使われます。

// 著作権 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

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}");
    }
}
  • IntoIterator は、for ループを動作させるトレイトです。これは Vec<T> のようなコレクション型や、それらへの参照である &Vec<T>&[T] によって実装されています。範囲もこれを実装しています。これが、for i in some_vec { .. } でベクタをイテレートできる一方で、some_vec.next() は存在しない理由です。

IntoIterator のドキュメントを参照してください。IntoIterator のすべての実装は、2 つの型を宣言しなければなりません。

  • Item: i8 のような、イテレート対象の型
  • IntoIter: into_iter メソッドが返す Iterator

IntoIterItem は結び付いていることに注意してください。イテレータは同じ Item 型を持たなければならず、これは Option<Item> を返すことを意味します

この例では、x 座標と y 座標のすべての組み合わせをイテレートしています。

main でグリッドを 2 回イテレートしてみてください。なぜこれは失敗するのでしょうか。IntoIterator::into_iterself の所有権を受け取ることに注意してください。

この問題は、&Grid に対して IntoIterator を実装し、参照でイテレートする GridRefIter を作成することで修正できます。GridIterGridRefIter の両方を含むバージョンは、この playground で利用できます。

同じ問題は標準ライブラリの型でも発生することがあります。for e in some_vectorsome_vector の所有権を取得し、そのベクタの所有された要素をイテレートします。代わりに for e in &some_vector を使うと、some_vector の要素への参照をイテレートできます。