Rustのイテレータ(Iterator)とその便利なメソッド

先生

Rustのイテレータはコレクション操作の救世主!効率的なデータ処理でコードをスッキリさせよう🚀

Rustのイテレータとは?基本と仕組み

Rustにおけるイテレータは、一連の値を順番に処理するための強力な仕組みです。コレクション(例えばVec、HashMapなど)の要素を一つずつ取り出す際に非常に役立ちます。イテレータを使うことで、ループ処理をより安全かつ効率的に記述できます。

イテレータはIteratorトレイトを実装した構造体であり、next()メソッドを提供します。next()メソッドは、次の要素をOption<T>型で返します。要素が存在する場合はSome(T)、終端に達した場合はNoneを返します。


rust
let v = vec![1, 2, 3];
let mut iter = v.iter();

assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);

この例では、v.iter()でベクタvのイテレータを作成しています。iter.next()を呼び出すことで、ベクタの要素が順番に取り出され、最後にNoneが返されます。

イテレータの便利なメソッド集

Rustのイテレータは、様々な便利なメソッドを提供しています。これにより、要素のフィルタリング、変換、集計などが簡潔に行えます。ここでは、特によく使われるメソッドを紹介します。

map()メソッドは、イテレータの各要素に関数を適用し、新しいイテレータを生成します。


rust
let v = vec![1, 2, 3];
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
assert_eq!(doubled, vec![2, 4, 6]);

この例では、各要素を2倍にするmap()を使用しています。.collect()は、イテレータの結果をコレクション(ここではVec<i32>)に変換します。

filter()メソッドは、指定された条件を満たす要素のみを含む新しいイテレータを生成します。


rust
let v = vec![1, 2, 3, 4, 5, 6];
let even: Vec<i32> = v.iter().filter(|x| x % 2 == 0).cloned().collect();
assert_eq!(even, vec![2, 4, 6]);

ここでは、偶数のみを抽出するfilter()を使用しています。.cloned()は、参照をコピーに変換するために使用します。

collect()メソッドは、イテレータの要素をコレクション(Vec, HashMap, Stringなど)に変換します。


rust
let v = vec![1, 2, 3];
let string: String = v.iter().map(|x| x.to_string()).collect();
assert_eq!(string, "123");

ここでは、数値を文字列に変換し、それらを連結して一つのStringを生成しています。

fold()メソッドは、イテレータの要素を累積して単一の値にします。


rust
let v = vec![1, 2, 3, 4];
let sum = v.iter().fold(0, |acc, x| acc + x);
assert_eq!(sum, 10);

この例では、fold()を使用してベクタの要素の合計を計算しています。最初の引数0は初期値、2番目の引数は累積関数です。

any()メソッドは、イテレータの少なくとも一つの要素が条件を満たすかどうかを判定します。all()メソッドは、すべての要素が条件を満たすかどうかを判定します。


rust
let v = vec![1, 2, 3, 4, 5];
let has_even = v.iter().any(|x| x % 2 == 0);
let all_positive = v.iter().all(|x| x > &0);
assert_eq!(has_even, true);
assert_eq!(all_positive, true);

イテレータと所有権

Rustのイテレータは所有権と密接に関連しています。iter()iter_mut()into_iter()の3種類のイテレータがあり、それぞれが異なる所有権の扱いをします。

iter(): 不変な参照を生成します。元のコレクションは変更されません。

iter_mut(): 可変な参照を生成します。元のコレクションを変更できます。

into_iter(): 所有権をムーブします。元のコレクションはイテレータに消費されます。


rust
let v = vec![1, 2, 3];

// iter()
for x in v.iter() { // vの不変な参照
    println!("{}", x);
}
println!("{:?}", v); // vは有効

// into_iter()
let mut v2 = vec![1,2,3];
for x in v2.into_iter() { // v2の所有権がmoveされる
    println!("{}", x);
}
//println!("{:?}", v2);  // v2はもう有効ではないので、コンパイルエラー

into_iter()を使用すると、イテレータがベクタの所有権を奪うため、その後のベクタへのアクセスはコンパイルエラーになります。

参考リンク

まとめ

Rustのイテレータは、コレクションを効率的に処理するための強力なツールです。map()filter()collect()fold()などの便利なメソッドを活用することで、簡潔で読みやすいコードを書くことができます。所有権の概念を理解し、iter()iter_mut()into_iter()を適切に使い分けることで、安全かつ効率的なイテレータ処理を実現できます。ぜひRustのイテレータを使いこなして、より高度なプログラミングに挑戦してください。