Rustにおけるイテレータ(miterator)を使用したシーケンス処理は、非常に強力で効率的な方法です。この記事では、Rustのイテレータを用いてシーケンスを処理する方法について、完全かつ包括的に解説します。Rustのイテレータを理解することで、コードの効率を高め、可読性を向上させることができます。ここでは、基本的なイテレータの使い方から、より高度な操作までを順を追って説明します。
1. イテレータの基本概念
Rustのイテレータは、コレクション(ベクター、配列、ハッシュマップなど)を順に処理するためのオブジェクトです。イテレータは、要素を順に返すnext
というメソッドを持っています。このメソッドはイテレータが一度に1つの値を返し、すべての要素が返された後はNone
を返すようになります。

rustlet vec = vec![1, 2, 3, 4, 5];
let mut iter = vec.iter(); // イテレータを作成
while let Some(value) = iter.next() {
println!("{}", value);
}
このコードは、ベクターの各要素を順に取り出して表示します。vec.iter()
はベクターの参照をイテレータに変換し、next()
メソッドで一つずつ値を取り出します。
2. イテレータのメソッド
Rustのイテレータは非常に多くの便利なメソッドを提供しています。これらのメソッドはチェーン可能で、関数型プログラミングのように、直感的にシーケンスを処理できます。
2.1. map()
メソッド
map()
は、各要素に対して変換を適用します。たとえば、数値のリストを2倍にする処理を行いたい場合、次のように書けます。
rustlet numbers = vec![1, 2, 3, 4, 5];
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
println!("{:?}", doubled); // [2, 4, 6, 8, 10]
map()
メソッドは、イテレータの各要素に指定したクロージャを適用し、新しいコレクションを作成します。この例では、x * 2
というクロージャを各要素に適用して2倍にしています。
2.2. filter()
メソッド
filter()
は、条件を満たす要素だけを抽出します。たとえば、偶数だけを抽出する場合、次のように書きます。
rustlet numbers = vec![1, 2, 3, 4, 5];
let even_numbers: Vec<i32> = numbers.iter().filter(|&x| x % 2 == 0).collect();
println!("{:?}", even_numbers); // [2, 4]
filter()
はクロージャを受け取り、その条件を満たす要素だけを通過させます。
2.3. fold()
メソッド
fold()
はイテレータの要素を1つずつ処理して、最終的な結果を生成します。例えば、合計値を求める場合は以下のように書けます。
rustlet numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers.iter().fold(0, |acc, x| acc + x);
println!("{}", sum); // 15
fold()
は2つの引数を取ります。1つ目は初期値、2つ目は各要素に適用する関数(クロージャ)です。この場合、合計を求めるために、前回の結果(acc
)に現在の要素(x
)を加算しています。
3. 組み込みのイテレータを使ったシーケンス処理
Rustでは標準ライブラリに多数の組み込みイテレータがあり、複雑な操作を簡単に行えます。例えば、range()
を使って数値のシーケンスを作成することができます。
rustfor i in 0..5 {
println!("{}", i); // 0 1 2 3 4
}
このコードは0..5
の範囲でイテレートし、0から4までの値を出力します。..
演算子は、指定した範囲の開始値から終了値までのシーケンスを作成します。
4. 高度なイテレータ操作
4.1. zip()
メソッド
zip()
は2つのイテレータを組み合わせて1つのイテレータにします。各要素はタプルとして返されます。
rustlet numbers = vec![1, 2, 3];
let letters = vec!['a', 'b', 'c'];
for (number, letter) in numbers.iter().zip(letters.iter()) {
println!("{} -> {}", number, letter);
}
// 出力:
// 1 -> a
// 2 -> b
// 3 -> c
このように、zip()
を使うことで、2つのシーケンスを並行して処理することができます。
4.2. clone()
とイテレータ
イテレータは基本的に1回しか使用できませんが、必要に応じて複製して使うことも可能です。例えば、イテレータを一時的に複製して、複数の場所で使用することができます。
rustlet numbers = vec![1, 2, 3];
let mut iter1 = numbers.iter();
let mut iter2 = iter1.clone();
println!("{:?}", iter1.next()); // Some(&1)
println!("{:?}", iter2.next()); // Some(&1)
このようにして、イテレータを複製し、同時に複数の場所で操作を行うことができます。
5. イテレータの効率性
Rustのイテレータは非常に効率的で、遅延評価(lazy evaluation)を行います。つまり、イテレータの操作は必要になるまで実行されず、最終的な結果を求めるときにのみ実行されます。この特徴により、無駄な計算を避け、効率的な処理が可能になります。
例えば、filter()
やmap()
メソッドを使用しても、実際に要素にアクセスするまでは計算が実行されません。これにより、パフォーマンスの向上が期待できます。
6. 結論
Rustのイテレータは、シーケンス処理において非常に強力で柔軟なツールです。基本的な使い方から、より高度な操作まで、さまざまな方法でイテレータを活用できます。効率的なデータ処理を行うためには、イテレータの特徴をよく理解し、適切に活用することが重要です。