Iterator トレイト

Iterator トレイトは、オブジェクトを値のシーケンスを生成するためにどのように使用できるかを定義します。たとえば、スライスの要素を生成できるイテレータを作成したい場合、次のようになります。

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

struct SliceIter<'s> {
    slice: &'s [i32],
    i: usize,
}

impl<'s> Iterator for SliceIter<'s> {
    type Item = &'s i32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i == self.slice.len() {
            None
        } else {
            let next = &self.slice[self.i];
            self.i += 1;
            Some(next)
        }
    }
}

fn main() {
    let slice = &[2, 4, 6, 8];
    let iter = SliceIter { slice, i: 0 };
    for elem in iter {
        dbg!(elem);
    }
}
  • SliceIter の例は、前のスライドで示した C スタイルの for ループと同じロジックを実装しています。

  • 受講者に、イテレータは遅延評価されることを指摘してください。イテレータを作成しても構造体が初期化されるだけで、それ以外の処理は行われません。next メソッドが呼び出されるまで、何の処理も発生しません。

  • イテレータは有限である必要はありません。永遠に値を生成し続けるイテレータも完全に有効です。たとえば、0.. のような半開区間は、整数オーバーフローが発生するまで進み続けます。

さらに調べる

  • SliceIter の「実際の」版は、標準ライブラリ内の slice::Iter 型です。ただし、実際の版では境界チェックをなくすために、内部ではインデックスの代わりにポインタを使用しています。

  • SliceIter の例は、参照を含む構造体の良い例であり、そのためライフタイム注釈を使用しています。

  • SliceIter にジェネリックパラメータを追加して、任意の種類のスライス(&[i32] だけでなく)で動作できるようにすることも示せます。