`while` と `loop`
Essential前提知識
イテレーション回数が事前にわからない——あるいは無限になりうる——ループが必要なとき、Rustは三つの構文を提供する:while、loop、そしてwhile letだ。
while
whileは各イテレーションの前に条件を評価し、条件が偽になった時点で終了する:
let mut n = 1;
while n < 100 {
n *= 2;
}
println!("{n}"); // 128
ifと同様に、条件はboolでなければならない。Rustは整数やポインタを自動でブール値に変換しない。
loop
loopはbreakするまで永遠に実行される:
loop {
let line = read_line();
if line == "quit" {
break;
}
process(line);
}
コンパイラはloopが必ず少なくとも一度実行されることを把握しているため、loopは値を生成できる唯一のループ構文だ。breakに値を渡す:
let result = loop {
let candidate = next_candidate();
if is_valid(candidate) {
break candidate; // `loop` 式全体の値になる
}
};
whileとwhile letはこれができない——条件が最初から偽の可能性があるため、生成する値が保証されない。
リトライループとイベントループ
loop + break valueはリトライパターンの慣用的な形だ:
let connection = loop {
match try_connect() {
Ok(conn) => break conn,
Err(e) => {
eprintln!("retrying after error: {e}");
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
};
終了シグナルが来るまで実行し続けるイベントループには、ミュータブルなフラグを使ったwhileより、break条件を持つloopの方が意図が明確に読める。
while let
while letはパターンがマッチし続ける間ループする:
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{top}");
}
stack.pop()はOption<i32>を返す:要素がある間はSome(v)、なくなるとNoneだ。pop()がNoneを返すとパターンのマッチが止まり、ループが終了する。
これはloopの中にmatch + breakを書いたものと等価だが、一目で意図が伝わる。if letの自然な仲間といえる——同じデシュガーを繰り返し行う形だ。
continue と break のラベル付き使用
入れ子になったループを抜け出すのは難しいことがある。ラベルを使うと特定のループを指定できる:
'outer: for row in &grid {
for &cell in row {
if cell == target {
break 'outer; // 両方のループを抜ける
}
}
}
ラベルはシングルクォートで始まり、ループキーワードの前に置く。break 'label valueでラベルと値を組み合わせ、ラベル付きloop式で値を生成できる。