このページの目次
Iterator とクロージャー
Iterator は Rust において最も成功した抽象化です。map/filter/chain などのアダプタは遅延評価(呼び出されない限り実行されない)であり、コンパイル時にインライン展開されて手書きのループと同等の機械語に最適化されます(ゼロコスト)。Fn/FnMut/FnOnce の3種類のクロージャートレイトは3つのキャプチャ方式に対応し、collect はイテレータをコンテナに収束させます。チェーン操作は読みやすさからゼロコストへ、一歩で到達します。
Iterator trait: 反復の根源
next() が唯一実装必須のメソッドです。他のすべてのメソッド(map, filter, take, fold, collect など)は、next() を基にしたデフォルト実装です。つまり、next() を実装するだけで、あなたの型は自動的にイテレータエコシステム全体を利用可能になります。
遅延評価: イテレータは「駆動」されるまで何もしない
let v: =
.filter // 発生しない — 単にチェーンを構築しているだけ
.map // 発生しない
.take // 発生しない
.collect; // ここで発生! すべてのアダプタが「駆動」される
// → [0, 4, 8]
このコードは collect() が呼ばれるまで、要素を1つとして処理しません。collect() が next() を呼び出すと、それは take に要求し、take は map に要求し、map は filter に要求し、filter は 0..10 に要求します。これにより「プル」型のチェーンが形成されます。これは Python のリスト内包表記のような即時評価(eager evaluation)とは根本的に異なります。
主要なアダプタ
| アダプタ | 役割 | 評価タイミング |
|---|---|---|
map | 各要素を変換 | 遅延 |
filter | 条件を満たす要素を保持 | 遅延 |
take(n) | 先頭 n 個を取得後、停止 | 遅延 |
skip(n) | 先頭 n 個をスキップ | 遅延 |
enumerate | インデックス (index, item) を付加 | 遅延 |
chain | 2つのイテレータを連結 | 遅延 |
flat_map | 各要素をイテレータに展開 | 遅延 |
fold | 累積(リデュース) | 即時(イテレータを消費) |
collect | コレクションとして収集 | 即時 |
3種類のクロージャートレイト: FnOnce → FnMut → Fn
これら3つのトレイトは制約のチェーンを形成します:
FnOnce: 1回しか呼び出せない — キャプチャした値を消費(所有権の移動)
FnMut: `&mut self` — キャプチャした値を変更可能、複数回呼び出し可能
Fn: `&self` — キャプチャした値は読み取り専用、複数回呼び出し可能、最も汎用的
- すべてのクロージャーはデフォルトで
FnOnceを実装します - クロージャーがキャプチャした値を消費しない場合 →
FnMutも実装 - クロージャーがキャプチャした値を変更しない場合 →
Fnも実装
let s = Stringfrom;
let f = ; // s がクロージャーにムーブされる — これは FnOnce
// f() は1回しか呼び出せない — 呼び出し後、s は消費され、f は再度呼び出せない
let mut v = vec!;
let mut f = ; // v を変更 — FnMut(複数回呼び出し可能)
let x = 42;
let f = ; // 読み取り専用 — 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