このページの目次

Iterator とクロージャー

Iterator は Rust において最も成功した抽象化です。map/filter/chain などのアダプタは遅延評価(呼び出されない限り実行されない)であり、コンパイル時にインライン展開されて手書きのループと同等の機械語に最適化されます(ゼロコスト)。Fn/FnMut/FnOnce の3種類のクロージャートレイトは3つのキャプチャ方式に対応し、collect はイテレータをコンテナに収束させます。チェーン操作は読みやすさからゼロコストへ、一歩で到達します。

Iterator trait: 反復の根源

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    // デフォルト実装を持つメソッドが70以上存在 — これらはすべて next() に基づいている
}

next() が唯一実装必須のメソッドです。他のすべてのメソッド(map, filter, take, fold, collect など)は、next() を基にしたデフォルト実装です。つまり、next() を実装するだけで、あなたの型は自動的にイテレータエコシステム全体を利用可能になります。

遅延評価: イテレータは「駆動」されるまで何もしない

let v: Vec<i32> = (0..10)
    .filter(|x| x % 2 == 0)         // 発生しない — 単にチェーンを構築しているだけ
    .map(|x| x * 2)                  // 発生しない
    .take(3)                         // 発生しない
    .collect();                      // ここで発生! すべてのアダプタが「駆動」される
// → [0, 4, 8]

このコードは collect() が呼ばれるまで、要素を1つとして処理しません。collect()next() を呼び出すと、それは take に要求し、takemap に要求し、mapfilter に要求し、filter0..10 に要求します。これにより「プル」型のチェーンが形成されます。これは Python のリスト内包表記のような即時評価(eager evaluation)とは根本的に異なります。

主要なアダプタ

アダプタ役割評価タイミング
map各要素を変換遅延
filter条件を満たす要素を保持遅延
take(n)先頭 n 個を取得後、停止遅延
skip(n)先頭 n 個をスキップ遅延
enumerateインデックス (index, item) を付加遅延
chain2つのイテレータを連結遅延
flat_map各要素をイテレータに展開遅延
fold累積(リデュース)即時(イテレータを消費)
collectコレクションとして収集即時

3種類のクロージャートレイト: FnOnce → FnMut → Fn

これら3つのトレイトは制約のチェーンを形成します:

FnOnce: 1回しか呼び出せない — キャプチャした値を消費(所有権の移動)
FnMut:  `&mut self` — キャプチャした値を変更可能、複数回呼び出し可能
Fn:     `&self` — キャプチャした値は読み取り専用、複数回呼び出し可能、最も汎用的
  • すべてのクロージャーはデフォルトで FnOnce を実装します
  • クロージャーがキャプチャした値を消費しない場合 → FnMut も実装
  • クロージャーがキャプチャした値を変更しない場合 → Fn も実装
let s = String::from("hi");
let f = || s;                        // s がクロージャーにムーブされる — これは FnOnce
// f() は1回しか呼び出せない — 呼び出し後、s は消費され、f は再度呼び出せない

let mut v = vec![1, 2, 3];
let mut f = || { v.push(4); };       // v を変更 — FnMut(複数回呼び出し可能)

let x = 42;
let f = || println!("{}", x);        // 読み取り専用 — Fn(最も汎用的)

ゼロコストのイテレーション

リリースモードでは、イテレータチェーンは通常、コンパイラによって単一のループに「融合」されます。map, filter, collect の連続する呼び出しは、追加の割り当てや仮想関数呼び出しなしで、等価な for ループに最適化されます。これが、イテレータにおける Rust のゼロコスト抽象化の現れです。

参考

  • Rust Book: 第13章
  • Rust Reference: Fn traits, Iterator

Keywords: Iterator, lazy evaluation, collect, Fn, FnMut, FnOnce, adaptor, zero-cost