Rustは、メモリ安全性や実行速度、そしてモダンな構文を兼ね備えたプログラミング言語として、多くの開発者から高い支持を得ています。
プログラムを開発する上で欠かせないのが「条件分岐」です。
特定の条件が満たされた場合にのみ特定の処理を実行するという制御フローは、あらゆるロジックの基盤となります。
Rustにおけるif else文は、他の言語と似た構文を持ちながらも、「式(Expression)である」という非常に強力な特徴を持っています。
この記事では、Rustでの条件分岐の基本から、式としての高度な活用方法、そして安全なコードを書くための注意点まで詳しく解説します。
Rustの制御構文をマスターし、より宣言的で美しいコードを書けるようになりましょう。
Rustにおけるif文の基本構文
Rustのif文は、指定した条件がtrueであるかどうかを判定し、実行するブロックを決定します。
基本的な書き方は非常にシンプルですが、Rust特有のルールがいくつか存在します。
if文のシンプルな構造
最も基本的なif文の形は以下の通りです。
fn main() {
let number = 10;
// 条件式を評価
if number > 5 {
println!("数値は5より大きいです");
}
}
数値は5より大きいです
ここで注目すべき点は、条件式を囲む丸括弧 ( ) が不要であること、そして実行するコードを囲む波括弧 { } が必須であることです。
C言語やJavaScriptでは処理が1行の場合に波括弧を省略できることがありますが、Rustではエラーの原因となるため必ず記述する必要があります。
条件式は必ずbool型である必要がある
Rustは静的型付けに非常に厳格な言語です。
そのため、if文の条件式には必ず bool 型(true または false)を返す式を記述しなければなりません。
例えば、C言語のように「数値の0以外は真、0は偽」といった暗黙的な型変換は行われません。
// NGな例:コンパイルエラーになります
let number = 3;
if number {
println!("このコードはコンパイルされません");
}
上記のコードをコンパイルしようとすると、expected bool, found integer というエラーが発生します。
JavaScriptやPythonから移行してきた開発者が最初につまずきやすいポイントですが、これにより意図しないバグの混入を未然に防ぐことができます。
else と else if を使った複数条件の分岐
一つの条件だけでなく、条件に合致しなかった場合の処理や、複数の条件を順番に評価したい場合には else や else if を使用します。
elseによる代替処理の実行
条件が偽(false)だった場合に実行する処理を記述します。
fn main() {
let age = 15;
if age >= 18 {
println!("成人です");
} else {
println!("未成年です");
}
}
未成年です
else ifによる多方向分岐
複数の条件を順番にチェックしたい場合は、else if を繋げます。
条件は上から順に評価され、最初に true になったブロックのみが実行されます。
fn main() {
let score = 85;
if score >= 90 {
println!("評価: A");
} else if score >= 80 {
println!("評価: B");
} else if score >= 70 {
println!("評価: C");
} else {
println!("評価: D以下");
}
}
評価: B
ただし、あまりにも多くの else if を重ねるとコードの可読性が低下します。
そのような場合は、Rustの強力な機能である match 式の利用を検討するのがRustらしい書き方(アイディオマティックな書き方)です。
Rustの真骨頂:式としてのif else
Rustの if は「文(Statement)」ではなく「式(Expression)」です。
これは、ifブロックの結果を値として返し、変数に代入できることを意味します。
他の言語における「三項演算子(condition ? a : b)」のような役割を、通常の if 文でこなすことができます。
変数への代入
以下の例では、条件に応じて変数に代入する値を決定しています。
fn main() {
let condition = true;
// ifの結果を変数に代入
let number = if condition { 5 } else { 6 };
println!("数値の結果: {}", number);
}
数値の結果: 5
ここで重要なのは、各ブロックの最後にセミコロン(;)を付けていない点です。
Rustでは、ブロックの最後の式にセミコロンを付けないことで、その値がブロック全体の評価値となります。
型の整合性に関する重要な制約
if を式として使用する場合、すべてのブランチ(if、else if、else)が返す値の型は一致していなければなりません。
// NGな例:型不一致でエラー
let result = if true {
10
} else {
"error" // 整数型を期待しているところに文字列型が来ているためエラー
};
Rustのコンパイラはコンパイル時に変数の型を確定させる必要があります。
もし条件によって型が変わってしまうと、プログラムの安全性が保証できなくなるため、このような厳格なチェックが行われます。
実践的な活用シーン
if else を単なる条件分岐としてだけでなく、Rustの特性を活かした書き方で活用する場面を紹介します。
関数の戻り値として利用する
関数内で条件によって戻り値を出し分ける際にも、if 式は役立ちます。
fn get_discount(price: u32) -> u32 {
// if式をそのまま関数の最後に置くことで戻り値にする
if price > 1000 {
100
} else {
0
}
}
fn main() {
let discount = get_discount(1500);
println!("割引額: {}円", discount);
}
割引額: 100円
複雑な初期化処理の簡略化
変数を一旦 mut(可変)で宣言してから後で書き換えるのではなく、if 式を使ってイミュータブル(不変)な変数として初期化することができます。
これにより、後から値が不意に変更されるリスクを減らせます。
| 悪い例(可変変数を使用) | 良い例(if式を使用) |
|---|---|
let mut x; if cond { x = 1; } else { x = 2; } | let x = if cond { 1 } else { 2 }; |
if let を使った高度なパターンマッチング
Rustには、特定のパターンに一致する場合のみ処理を行う if let という特殊な構文があります。
これは Option 型や Result 型から値を取り出す際に非常に便利です。
fn main() {
let some_value: Option<i32> = Some(42);
// Someの中に値がある場合だけ実行
if let Some(x) = some_value {
println!("値は {} です", x);
} else {
println!("値はありません");
}
}
値は 42 です
通常の if 文では列挙型(Enum)の中身を直接扱うのは難しいですが、if let を使うことで、特定のケースだけを簡潔に記述し、それ以外を else で受け流すといった柔軟な制御が可能になります。
if else と match の使い分け
Rustにはもう一つの強力な制御フローである match があります。
どちらを使うべきか迷った際は、以下の基準を参考にしてください。
if else が適している場合
- 条件が単純な真偽値(
true/false)の場合。 - 比較演算子(
>=,==,&&など)を組み合わせた複雑な論理式が必要な場合。
- 条件が単純な真偽値(
match が適している場合
- 列挙型(Enum)のすべてのパターンを網羅したい場合。
- 3つ以上の分岐があり、それぞれが特定のパターンに基づいている場合。
- 値の構造分解(Destructuring)を同時に行いたい場合。
Rustのコンパイラは、match を使用すると「すべてのケースが網羅されているか」をチェックしてくれます。
if else では最後の else を書き忘れても(代入式でなければ)エラーになりませんが、match は網羅性を保証するため、より安全なプログラミングが可能です。
まとめ
Rustの if else 文は、単なる制御フローの道具ではなく、値を生成する「式」としての側面を持つ非常に柔軟な機能です。
本記事で解説した重要なポイントを振り返ります。
- 条件式は必ず bool 型であること(他の型からの暗黙的な変換はない)。
- 波括弧 { } は必須であり、丸括弧 ( ) は不要。
- ifは式であり、変数への代入や関数の戻り値として利用可能。
- 全ブランチの型を一致させる必要がある(式として使用する場合)。
- より複雑なパターンには
if letやmatchを検討する。
これらの特性を理解し、適切に使い分けることで、Rustの持つ「安全性」と「表現力」を最大限に引き出すことができます。
最初は型の厳格さに戸惑うかもしれませんが、慣れてくればコンパイラが論理的なミスを未然に防いでくれる心強さを実感できるはずです。
より詳細な情報については、Rust公式ドキュメント(The Rust Programming Language)なども併せて参照してください。
