`if` 式
Essential前提知識
Rustのifは単なる条件文ではなく、値を生成する式だ。このひとつの性質が、代入・関数本体・条件ロジックの書き方をすべて変える。
基本的な形
条件式はboolでなければならない。Rustは整数やポインタなどを自動でブール値に変換しないため、nが整数のときif nはコンパイルエラーになる——if n != 0と書くこと。
if temperature > 100.0 {
println!("too hot");
} else if temperature < 0.0 {
println!("too cold");
} else {
println!("just right");
}
値を生成する式としての if
ifは式なので、その結果を束縛に直接代入できる:
let label = if n > 0 { "positive" } else { "non-positive" };
すべての分岐は同じ型を生成しなければならない。型が一致しない分岐はコンパイルエラーになる:
let x = if flag { 1 } else { "text" }; // error: expected integer, found `&str`
各分岐はブロックであり、ブロックの値は最後の式(末尾にセミコロンなし)だ。末尾にセミコロンを付けると式が文になり、ブロックは()を返す。宣言された型が()でない場合は型エラーになる:
let x = if flag { 1 } else { 2; }; // error: expected i32, found ()
// ^ セミコロンが 2 を捨てる
else の省略
ifを文として使う場合——その値が捨てられる場合——else分岐を省略できる。if分岐と暗黙のelse省略の両方が()を返すので、型が一致する:
if verbose {
println!("debug: starting");
}
このような形を式として使おうとすると、コンパイラはelseを要求する:
let x = if flag { 1 }; // error: `if` may be missing an `else` clause
早期リターン — ガード節
Rustの関数ではreturnは早期終了にのみ使う。よく使われるパターンがガード節(guard clause)だ:先頭で事前条件を確認し、条件を満たさない場合は即座に抜ける:
fn parse_positive(s: &str) -> Option<u32> {
if s.is_empty() {
return None;
}
let n: u32 = s.parse().ok()?;
if n == 0 {
return None;
}
Some(n)
}
ガード節はハッピーパスのネストを浅く保ち、読みやすくする。ループ内では同じイディオムでreturnの代わりにcontinue(現在のイテレーションをスキップ)やbreak(ループを抜ける)を使う。
if let
if letはパターンと条件を組み合わせる:パターンがマッチしたときだけブロックが実行される。
let config: Option<&str> = Some("debug");
if let Some(level) = config {
println!("log level: {level}");
}
これは意味のあるアームが一つのmatchの短縮形だ。elseを追加してマッチしない場合を処理することもできる:
if let Some(level) = config {
println!("log level: {level}");
} else {
println!("using default log level");
}
if letも通常のifと同じく式なので、両分岐の型が一致していれば結果を束縛に代入できる。if letはOption・Result・ペイロードを持つ列挙型など、特定の一つのバリアントだけに関心がある状況で頻繁に使われる。パターン全般についてはパターンマッチングで扱う。