`for` ループ

Essential
最終更新: タグ: Rust

forはシーケンスに変換できる任意の値をイテレートする。単純なループではwhileより簡潔に書け、等価なイテレータパイプラインより所有権が明示的だ。

範囲のイテレーション

for i in 0..5 {
    println!("{i}"); // 0, 1, 2, 3, 4
}

0..5は半開区間の範囲(range)だ:開始を含み、終端を含まない。終端を含めるには0..=5を使う。範囲はRustの標準ライブラリに独自の型として存在し、IntoIteratorを実装しているため、forにそのまま渡せる。

三種の所有権の形

ループ変数とコレクションの関係はinの後ろに何を渡すかで決まる:

形式コレクションへの影響要素の型
for x in vvはムーブ(消費)されるT
for x in &vvはイミュータブルに借用される&T
for x in &mut vvはミュータブルに借用される&mut T
let names = vec!["Alice", "Bob", "Carol"];

// 借用 — ループ後も names は使える
for name in &names {
    println!("{name}");
}

// ムーブ — names は消費される。この後は使えない
for name in names {
    println!("{name}");
}

スライス&[T])は常にfor x in sliceの形でイテレートする(sliceはすでに&[T]だ)——スライスはデータを所有していないため、値渡しのIntoIteratorを実装しない。

要素をその場で変更するにはミュータブルに借用する:

let mut values = vec![1, 2, 3];
for x in &mut values {
    *x *= 2; // &mut i32 越しに代入するため参照外しが必要
}
// values は [2, 4, 6] になる

for でのパターンのデストラクチャリング

ループ変数は単なる名前ではなくパターンだ。タプルを直接デストラクチャリングできる:

let pairs = vec![(1, 'a'), (2, 'b'), (3, 'c')];

for (n, c) in &pairs {
    println!("{n}: {c}");
}

enumeratezip

forと一緒に、フルパイプラインを組まなくても直接使えるイテレータアダプターが二つある:

let fruits = ["apple", "banana", "cherry"];

// 各要素と0始まりのインデックスをペアにする
for (i, fruit) in fruits.iter().enumerate() {
    println!("{i}: {fruit}");
}
let nums = [1, 2, 3];
let chars = ['x', 'y', 'z'];

// 二つのコレクションを同時に走査する。短い方で止まる
for (n, c) in nums.iter().zip(chars.iter()) {
    println!("{n}-{c}");
}

イテレータアダプターのAPI全体についてはイテレータで扱う。

breakcontinue

breakはループを即座に終了し、continueは現在のイテレーションの残りをスキップする:

for n in 0..20 {
    if n % 2 == 0 { continue; } // 偶数をスキップ
    if n > 10     { break; }    // n が 10 を超えたら終了
    println!("{n}");
}

loopと違い、forループはbreak valueで値を生成できない——値が必要ならloopを使うこと。