`while` と `loop`

Essential
最終更新: タグ: Rust

前提知識

イテレーション回数が事前にわからない——あるいは無限になりうる——ループが必要なとき、Rustは三つの構文を提供する:whileloop、そしてwhile letだ。

while

whileは各イテレーションの前に条件を評価し、条件が偽になった時点で終了する:

let mut n = 1;
while n < 100 {
    n *= 2;
}
println!("{n}"); // 128

ifと同様に、条件はboolでなければならない。Rustは整数やポインタを自動でブール値に変換しない。

loop

loopbreakするまで永遠に実行される:

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` 式全体の値になる
    }
};

whilewhile 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の自然な仲間といえる——同じデシュガーを繰り返し行う形だ。

continuebreak のラベル付き使用

入れ子になったループを抜け出すのは難しいことがある。ラベルを使うと特定のループを指定できる:

'outer: for row in &grid {
    for &cell in row {
        if cell == target {
            break 'outer; // 両方のループを抜ける
        }
    }
}

ラベルはシングルクォートで始まり、ループキーワードの前に置く。break 'label valueでラベルと値を組み合わせ、ラベル付きloop式で値を生成できる。