`for` ループ
Essential前提知識
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 v | vはムーブ(消費)される | T |
for x in &v | vはイミュータブルに借用される | &T |
for x in &mut v | vはミュータブルに借用される | &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}");
}
enumerate と zip
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全体についてはイテレータで扱う。
break と continue
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を使うこと。