Rustのイテレータ
イテレータ(iterator)とは、値を1つずつ順番に取り出せるもののことです。じつは for ループのページから、名前を知らないまま使ってきました。コレクションを for ループで回すとき、裏ではいつもイテレータが動いているのです。
イテレータを取り出す
Section titled “イテレータを取り出す”fn main() { let numbers = vec![1, 2, 3]; let mut iter = numbers.iter();
println!("{:?}", iter.next()); // Some(1) println!("{:?}", iter.next()); // Some(2) println!("{:?}", iter.next()); // Some(3) println!("{:?}", iter.next()); // None}実行結果:
Some(1)Some(2)Some(3)None.next() は Option を返します。要素があるあいだは Some(値)、なくなったら None です。for ループの正体は、None が返ってくるまで裏で .next() を何度も呼び出しているだけなのです。
map:すべての要素を変換する
Section titled “map:すべての要素を変換する”.map() はクロージャを受け取り、それをすべての要素に適用して、新しいイテレータを作ります。
fn main() { let numbers = vec![1, 2, 3, 4, 5]; let doubled: Vec<i32> = numbers.iter().map(|n| n * 2).collect();
println!("{:?}", doubled);}実行結果:
[2, 4, 6, 8, 10].collect() は、イテレータを具体的なコレクション(ここでは Vec<i32>)に戻すメソッドです。これがないと、まだ何も計算されていないイテレータが置いてあるだけになってしまいます。
filter:一部の要素だけを残す
Section titled “filter:一部の要素だけを残す”fn main() { let numbers = vec![1, 2, 3, 4, 5, 6]; let evens: Vec<&i32> = numbers.iter().filter(|n| **n % 2 == 0).collect();
println!("{:?}", evens);}実行結果:
[2, 4, 6]**n と * が2つ並んでいるのは見慣れないかもしれません。.filter() はクロージャに「参照の参照」(&&i32)を渡すので、ふつうの数と比べるには2回参照外しをする必要があるのです。よくある小さなクセなので、今はくわしい理由まで気にしなくて大丈夫です。
map と filter をつなげる
Section titled “map と filter をつなげる”ここからが、イテレータのメソッドをつなげる(チェーンする)ことの本領です。いくつもの手順を、読みやすいひとつの流れにまとめられます。
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers .iter() .filter(|n| **n % 2 == 0) // 偶数だけを残す .map(|n| n * 10) // それぞれを10倍する .collect();
println!("{:?}", result);}実行結果:
[20, 40, 60]同じ処理を、for ループと空の Vec への push で書いた場合と比べてみてください。それも間違いではありません。でもチェーンで書いたほうは「絞り込んで、10倍して、集める」という、やりたい手順そのままの順番で読めます。
ほかによく使うメソッド
Section titled “ほかによく使うメソッド”fn main() { let numbers = vec![1, 2, 3, 4, 5];
let total: i32 = numbers.iter().sum(); let count = numbers.iter().count(); let has_even = numbers.iter().any(|n| n % 2 == 0);
println!("合計: {total}、個数: {count}、偶数を含む: {has_even}");}実行結果:
合計: 15、個数: 5、偶数を含む: true.sum() はすべての要素の合計、.count() は要素の数を返します。.any() は、条件を満たす要素が1つでもあれば true を返します。これらは、どのイテレータにもあるメソッドのほんの一部です。ほかにも enumerate、zip、take、rev など、たくさんあります。独自のイテレータのページでは自分でイテレータを作り、これらのメソッドがすべて無料でついてくることを確かめます。
1. 奇数の2乗の合計
Section titled “1. 奇数の2乗の合計”1から10までの数から奇数だけを残し、それぞれを2乗して、全部足し合わせてください。mut の変数は使わずに、一つのイテレータのチェーンで書きます。ヒント:.filter()、.map()、.sum() をつなげて、結果には let total: i32 のように型をつけます。
解答を見る
fn main() { let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let total: i32 = numbers .iter() .filter(|n| **n % 2 == 1) // 1, 3, 5, 7, 9 を残す .map(|n| n * n) // 1, 9, 25, 49, 81 .sum();
println!("奇数の2乗の合計: {total}");}実行結果:
奇数の2乗の合計: 165total に : i32 が必要なのは、.sum() がいくつもの数値型に合計できるので、どれがほしいのかRustには推測できないからです。
2. 長い単語を大文字で叫ぶ
Section titled “2. 長い単語を大文字で叫ぶ”vec!["the", "quick", "brown", "fox", "jumps"] から始めます。3文字より長い単語だけを大文字にした新しい Vec<String> を作り、{:?} で表示してください。(文字数で選び、大文字に変換する問題なので、単語は英語のままにしています。日本語の文字は1文字が3バイトで、大文字もありません。)ヒント:.filter(|w| w.len() > 3) で長い単語を残し、.to_uppercase() で &str を大文字の String に変換します。
解答を見る
fn main() { let words = vec!["the", "quick", "brown", "fox", "jumps"];
let loud: Vec<String> = words .iter() .filter(|w| w.len() > 3) .map(|w| w.to_uppercase()) .collect();
println!("{:?}", loud);}実行結果:
["QUICK", "BROWN", "JUMPS"]words 自体は変わりません。.iter() は借用するだけなので、チェーンのあとでも words を使えます。
- イテレータは
.next()で値を1つずつ取り出します。.next()はOptionを返します。 forループの裏ではイテレータが動いています。.map()は各要素を変換し、.filter()は条件に合う要素だけを残し、.collect()は結果を具体的なコレクションに戻します。- イテレータのメソッドをチェーンするほうが、可変の変数に結果をためていく手書きのループより、たいていわかりやすくなります。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. イテレータの .next() は何を返しますか?
2. .iter().map(...).collect() のようなチェーンの最後にある .collect() は何をしますか?
3. .filter(|n| **n % 2 == 0) で、n を2回参照外ししているのはなぜですか?
4. for ループでコレクションを回すとき、裏で動いているのは何ですか?
5. .filter() の結果のイテレータに残るのはどの要素ですか?
スコア: 0 / 5
さらに詳しく
Section titled “さらに詳しく”- 一連の要素をイテレータで処理する — The Rust Programming Language 日本語版:
next、map、filter、collectを、さらに多くの例で紹介する公式の入門です。 - パフォーマンス比較:ループVSイテレータ — The Rust Programming Language 日本語版:イテレータのチェーンが手書きのループと同じくらい速い理由。
Iteratorトレイト — 標準ライブラリのドキュメント(英語):イテレータのメソッドの完全な一覧で、それぞれに小さな例がついています。眺めるだけでも楽しいです。- イテレータ — Rust By Example 日本語版:フィボナッチ数列のイテレータを作る、短くて実行できる例。