📘

Rust イテレータ操作まとめ|よく使う16のメソッド+実例

2025/03/25に公開
1

はじめに

Rustを勉強して感じたのが、「イテレータって便利だけど、最初はちょっと難しい」ということでした。

.iter().into_iter().map().filter() など、よく使われるけれど混乱しやすいメソッドがたくさんあって、私自身もよくつまずきました。

この記事は、自分自身が勉強したこと・つまずいたこと・「これ分かりやすかった!」という気づきを整理してまとめたものです。

同じようにRustを学んでいる方の少しでもヒントになれば嬉しいです。


1. .iter() / .into_iter() / .iter_mut() - 文字列をイテレータに変換

  • .iter()- 中身を覗くだけのイテレータ

    • データを”借りて”読み取るだけで、元の値はそのまま残る。
    • 「ちょっと中身を見て処理したいとき」に使う。
    • .iter()値の参照 (&i32) を返す。

  • .into_iter()- 中身を持っていくイテレータ

    • ベクタや配列の中身(値そのもの)を取り出して処理する。
    • 取り出したら元の変数は使えなくなる(使いきるイメージ)
    • .into_iter()値そのもの(所有権)を返す。

  • .iter_mut()- 中身を書き換えるためのイテレータ

    • データを”借りて”、その場で編集(更新)できるようにする。
    • 値を変えながら処理したいときに使う。
    • .iter_mut()値の可変参照(&mut i32)を返す。

【補足】イテレータって何だっけ?
コレクション(ベクタやハッシュマップなど)の要素を1つずつ取り出すための仕組み。

【補足】この場合の&はどういう意味?
この値、借りていい?」という意味で、変数の参照(ポインタ)を作るために使います。それでmutが後ろに付いた場合、「この値、借りて変更していい?」という意味になります。

サンプルプログラム

fn main() {
    let v: Vec<i32> = vec![1, 2,3];
    // .iter()の例
    for x in v.iter() {     // 値の参照 (&i32) を返す
        println!("{}", x);  // → 1, 2, 3(*をつけなくても動く)
        println!("{}", *x); // → 1, 2, 3(明示的につける場合。説明下記参照)
    }

    // .into_iter()の例
    for x in v.clone().into_iter() { // 値の所有権 (i32) を返す
        println!("{}", x);           // → 1, 2, 3
    }

    // .iter_mut()の例
    let mut v = vec![1, 2, 3];
    for x in v.iter_mut() {          // 値の可変参照 (&mut i32) を返す
        *x *= 2;
    }
}

【補足】 *(アスタリスク)= 借りたものから中身を取り出す
v.iter()が返す値は、 (&i32)です。そのため、値を取り出す場合は*をつける必要がありますが、Rustの場合*をつけなくても、Display トレイトを実装している型であれば、中身を自動的に取り出して表示してくれるようです。

ユースケース

メソッド 処理内容 主な用途 応用例 元のデータ
.iter() 中身を覗く(借用) 読み取りだけ 合計・平均・検索 残る
.into_iter() 中身を持っていく 値を使い切りたいとき フィルタ、文字列操作、型変換 消える
.iter_mut() 中身をいじる(借用) 値を変更したいとき データ変換、条件付き修正、正規化 残る

応用例

  1. iter()の応用:平均値を計算する(所有権を奪わず読み取る)
fn average(v: &Vec<i32>) -> f32 {
    let sum: i32 = v.iter().sum();
    sum as f32 / v.len() as f32 // キャストして返す
}

fn main() {
    let v = vec![10, 20, 30];
    println!("平均: {}", average(&v)); // → 20.0
}
  1. .into_iter() の応用:値の所有権ごと取り出す(「値そのものを取り出す=所有権を奪う」)
fn main() {
    let data = vec![Some("apple".to_string()), None, Some("banana".to_string())];

    let result: Vec<String> = data
        .into_iter() // 所有権を奪う
        .filter_map(|x| x) // Someだけ残す
        .collect();

    println!("{:?}", result); // → ["apple", "banana"]
}
  1. .iter_mut() の応用:値の書き換え(その1)
  • numbers.iter_mut()Vec<i32> に対して、要素へのミュータブルな参照のイテレータを返します。
  • つまり、n は ベクタ内の i32 を“借用している”(=アドレスを受け取っている)
  • nはアドレスなので、値に対して演算子する場合は、nの前に*を付ける必要がある。
fn main() {
    let mut numbers = vec![1,2,3,4,5];
    for n in numbers.iter_mut() { // numbers.iter_mut() は Vec<i32> に対して、要素へのミュータブルな参照のイテレータを返します。
        if *n % 2  == 0 {
            *n *= 10;
        }
    }

    println!("{:?}", numbers); // → [1, 20, 3, 40, 5]
}
  1. .iter_mut() の応用:値の書き換え(その2)
n main() {
    let mut words = vec!["hello".to_string(), "rust".to_string()];

    for word in words.iter_mut() {
        *word = word.to_uppercase();
    }

    println!("{:?}", words); // → ["HELLO", "RUST"]
}

2. .map() - 各要素を変換

下記のvsquaredの所有権は、独立している。
.collect() は新しい Vec<i32> を生成して、それを squared に格納する。

fn main() {
    // vとsquaredの所有権は、独立している。.collect() は新しい Vec<i32> を生成して、それを squared に格納
    let v = vec![1, 2, 3];
    let squared: Vec<_> = v.iter().map(|x| x * x).collect();
    println!("{:?}", squared);  // → [1, 4, 9]
}

ちなみに、mapの引数には&i32が渡ってくるので、こういう書き方もできる。

  let squared: Vec<_> = v.iter().map(|x| *x * *x).collect();

3. .filter() - 条件に合うものだけ残す

.filter()は、vの要素(1, 2, 3, 4, 5)を1つずつ取り出して使う。

残ったものを、新しいVecを生成して、それをeventに格納する。
こちらも同様、veventの所有権は独立している。

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    // vの要素(1, 2, 3, 4, 5)を1つずつ取り出して使う(所有権を奪う
    // 残ったものをVecに再構成する
    let event: Vec<_> = v.into_iter().filter(|x| x % 2 == 0).collect();
    println!("{:?}", event);    // → [2, 4]
}

4. .enumerate() - (インデックス, 要素) に変換

ベクタのインデックスを参照したい場合に使用する。

fn main() {
    let v = vec!["a", "b", "c"];
    for (i, val) in v.iter().enumerate() {
        println!("{}: {}", i, val); // → 0: a, 1: b, 2: c
    }
}

ちなみに、こういう書き方もできる。


    for (i, val) in v.iter().enumerate() {
        println!("{}: {}", i, *val); // → 0: a, 1: b, 2: c
    }

.enumerate()を使用して、要素を更新したい場合は.iter_mut()に変更する。

サンプルコード:偶数番目だけ2倍にする

fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];
    for (i, val) in numbers.iter_mut().enumerate() {
        if i % 2 == 0 {
            *val *= 2;
        }
    }
    println!("{:?}", numbers); // → [2, 2, 6, 4, 10]
}

5. .fold() - 初期値から集約(畳み込み)

  • iter.fold(初期値, |累積値, 要素| { 処理 })
  • 下記のプログラムは、ベクタ [1, 2, 3] の要素をひとつずつ処理して、全部足し合わせている。
  • accは、初期値0から始まり、各要素xを加算していく
  • xは、vの要素(1, 2, 3)を1つずつ取り出して使う(所有権を奪う)
  • fold().into_iter()でも問題なく動きます。into_inter()iter()の違いは、実行後に元のVecのデータが消えるか、残るかの違い。
    • into_iter()は、元のVecのデータは消える
    • iter()は、元のVecのデータは残る
fn main() {
    let sum = vec![1, 2, 3]
        .iter() // → Iterator<Item = &i32>
        .fold(
            0, // 初期値:0
            |acc, x|  // acc: 累積値, x: &i32(今の要素)
          acc + x,
        ); // 足し算して次のaccに渡す
    println!("{}", sum); // → 6
}

実際の処理の流れ

初期値 acc = 0

1回目: acc = 0, x = &1 → acc + x = 1
2回目: acc = 1, x = &2 → acc + x = 3
3回目: acc = 3, x = &3 → acc + x = 6

→ 結果:6

6. .reduce() - 「最初の2つの値からスタートして、順番に1つずつ合体していく」処理で、最終的に 1つの結果を返してくれる(なにもなければ None)。

reduce() の戻り値は Option<T> になります。なぜ、Option<T>なのかというと、要素が1つもない場合に、返す値が存在しないからです。

fn main() {
    let max: Option<i32> = vec![3, 6, 1, 8].into_iter().reduce(|a, b| a.max(b));
    println!("{:?}", max); // → Some(8)
}

7. .take(n) / .skip(n) - 要素を一部だけ処理

  • .take(n) は、最初のn個の要素を取り出す。
  • .skip(n)は、最初のn個の要素をスキップする。
fn main() {
    let v = vec![1, 2, 3, 4, 5];
    // take(n) は、最初のn個の要素を取り出す
    println!("{:?}", v.iter().take(3).collect::<Vec<_>>()); // [1, 2, 3]
    // skip(n) は、最初のn個の要素をスキップする
    println!("{:?}", v.iter().skip(2).collect::<Vec<_>>()); // [3, 4, 5]
}

8. .any() / .all() - 条件判定(bool)

  • .any()は、イテレータの要素のうち、1つでも条件を満たすものがあれば**true**を返す。
  • .all()は、イテレータの要素が全て条件を満たす場合に**true**を返す。
fn main() {
    let v = vec![1,2,3];

    println!("{}", v.iter().any(|&x| x > 2)); // → true
    // 又は println!("{}", v.iter().any(|x| *x > 2)); // → true
    
    println!("{}", v.iter().all(|&x| x > 2)); // → false
    // 又は、println!("{}", v.iter().all(|x| *x > 2)); // → false
}

【補足】&を付けた引数で受け取る場合の挙動はどうなるの?
結論:引数に&をつけると参照を外して、値そのものを受け取る意味合いになる。

  • |x| → xは&32(数値への参照)
  • |&x| → 渡してあげるときに「参照じゃなくて、値そのものを渡して」という意味

✅ もし |x| だけにしたら?
明示的に*xして使う必要があります。

v.iter().any(|x| *x % 2 == 0);

9. .find() / .postion() - 条件に合う最初の要素/位置

  • .find() は、条件を満たす最初の要素をSomeで包んで返す。
  • .postion() は、条件を満たす最初の要素のインデックスをSomeで包んで返す。
fn main() {
    let v = vec![10, 20, 30];

    let found = v.iter().find(|&&x| x == 20);
    // 又はlet found = v.iter().find(|x| **x == 20);
    println!("{:?}", found); // → Some(&20)

    let pos = v.iter().position(|&x| x == 20);
    // 又は let pos = v.iter().position(|x| *x == 20);
    println!("{:?}", pos); // → Some(1)
}

【補足】&&xって何だ??
.any.allの補足で、&xは参照じゃなくて、もう中身だけ欲しい(=値そのもの)ということでした。ところが、.findの場合は、要素そのものではなく、その要素への参照を意味するものを引数として渡し、処理したいので&&がつきます。
というのも、findは下記のような感じで実装されていて(イメージ↓)
find関数の処理の中で&item を渡すようなことをやってます。
そのため、最終的にクロージャには&&i32(参照の参照)が渡ってくるわけです。なのでクロージャの引数が|&x|にするとエラーになる。また|x|で受け取って処理する場合は、**をつける必要がある。

   fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
   where
       P: FnMut(&Self::Item) -> bool,
   {
       for item in self {
           if predicate(&item) {
               return Some(item);
           }
       }
       None
   }

10. .rev() - 逆順に処理

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    for x in v.iter().rev() {
        println!("{}", x); // → 5, 4, 3, 2, 1
    }
}

11. .zip() - 2つのイテレータを並べて、同時に1つずつ取り出してペアにする

.zip() は、2つのイテレータから1つずつ要素を取り出して、タプルにして返す。
2つのイテレータの要素数が異なる場合、短い方に合わせる。

fn main() {
    let a = vec![1, 2, 3, 4];
    let b = vec!["a", "b", "c"];
    // zip() は、2つのイテレータから1つずつ要素を取り出して、タプルにして返す
    // 2つのイテレータの要素数が異なる場合、短い方に合わせる
    for (n, s) in a.iter().zip(b.iter()) {
        println!("{} - {}", n, s);  // → 1 - a, 2 - b, 3 - c
    }
}

応用例:

✅ 2つのリストで「成績表」を作る

例えば、名前と点数のベクタから、メッセージを作りたい

fn main() {
    let names = vec!["山田", "佐藤", "鈴木"];
    let scores = vec![80, 75, 90];
    for (name, score) in names.iter().zip(scores.iter()) {
        println!("{}さんの点数は{}です", name, score);  // → 山田さんの点数は80です, 佐藤さんの点数は75です, 鈴木さんの点数は90です
    }
}

12. .chain() - イテレータを連結

.chain() は、2つのイテレータを連結して1つのイテレータにする。
2つのイテレータの要素数が異なる場合、最初のイテレータから順に取り出す。

fn main() {
    let a = vec![1, 2, 3, 4];
    let b = vec![5, 6, 7, 8];
    // chain() は、2つのイテレータを連結して1つのイテレータにする
    // 2つのイテレータの要素数が異なる場合、最初のイテレータから順に取り出す
    let combined: Vec<_> = a.into_iter().chain(b.into_iter()).collect::<Vec<_>>();
    println!("{:?}", combined); // → [1, 2, 3, 4, 5, 6, 7, 8]
}

13. .flat_map() - ネストされたイテレータをフラットにする

.flat_map() は、各要素をイテレータに変換して、それを1つのイテレータに連結する。
この場合、各要素を空白文字で分割して、それを1つのイテレータに連結している。

fn main() {
    let words = vec!["a b", "c d"];
    // flat_map() は、各要素をイテレータに変換して、それを1つのイテレータに連結する
    // この場合、各要素を空白文字で分割して、それを1つのイテレータに連結している
    let chars: Vec<_> = words.iter().flat_map(|s| s.split_whitespace()).collect();
    println!("{:?}", chars); // ["a", "b", "c", "d"]
}

応用例:

✅ 文字列リストからすべての単語を取り出す

例えば、複数の文から、単語だけ全部取り出したい
💡 各文を単語に分け、それを平坦にひとつのリストにするのが flat_map!

fn main() {
    let sentences = vec![
        "Rust is a systems programming language",
        "It is blazingly fast",
        "It is memory efficient",
    ];
    let words: Vec<&str> = sentences
        .iter()
        .flat_map(|s| s.split_whitespace())
        .collect::<Vec<&str>>();
    println!("{:?}", words); // → ["Rust", "is", "a", "systems", "programming", "language", "It", "is", "blazingly", "fast", "It", "is", "memory", "efficient"]
}


14. .collect() - ベクタや文字列に変換

fn main() {
    let v: Vec<_> = (1..=10).collect::<Vec<_>>();
    println!("{:?}", v); // → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    let s : String = "Rust".chars().collect::<String>();
    println!("{}", s); // → Rust
}

15. .count() / .sum() / .product()

  • .count() は、イテレータの要素数を返す。
  • .sum() は、イテレータの要素を足し合わせる。
  • .product() は、イテレータの要素を掛け合わせる。
fn main() {
    let v: Vec<i32> = vec![1, 2, 3, 4, 5];
    // count() は、イテレータの要素数を返す
    // sum() は、イテレータの要素を足し合わせる
    // product() は、イテレータの要素を掛け合わせる
    println!("{}", v.iter().count());   // → 5
    println!("{}", v.iter().sum::<i32>()); // → 15
    println!("{}", v.iter().product::<i32>());  // → 120
}

16. .inspect() - デバッグ用の中間処理

.inspect() は、各要素を取り出して、その値を表示する。用途はデバッグ。

fn main() {
    let v = vec![1, 2, 3, 4, 5];
    // inspect() は、各要素を取り出して、その値を表示する。用途はデバッグ
    let doubled: Vec<_> = v.iter().inspect(|x| println!("値: {}", x)).map(|x| x * 2).collect();
    println!("{:?}", doubled); // → [2, 4, 6, 8, 10]
}

応用例:

✅ map/filterチェーン中にデバッグしたい
例えば、今どういうデータが流れてるか確認したい時とかに便利

fn main() {
    let result: Vec<_> = (1..=5)
        .inspect(|x| println!("現在の値: {}", x))
        .map(|x| x * 2)
        .inspect(|x| println!("2倍の値: {}", x))
        .filter(|&x| x > 5)
        .collect::<Vec<_>>();
    println!("{:?}", result); // → [6, 8, 10]
}

💡 .inspect() はログのように途中経過を観察できる最強デバッグツール!

しかもチェーンの流れを壊さない。


🎉 おわりに(まとめ)

この記事で紹介した内容は、私自身がRustを学びながら「これ、よく出てくるな」と思ったメソッドたちです。

特に .map().filter().zip().inspect() などは、実際に使いこなせるとコードの表現力がぐっと広がります。

まだまだ学び途中ではありますが、これからRustを触ってみたい方や、同じように勉強中の方と一緒に成長していけたらと思っています。

最後まで読んでいただき、ありがとうございました。

少しでも参考になっていれば幸いです。お互いRustライフ楽しんでいきましょう!

1

Discussion