第8章: 制御構造
学習目標
- if文とif式の使い方を理解する
- ループ(loop、while、for)を使いこなす
- match式によるパターンマッチングを学ぶ
- 制御フローの高度なテクニックを把握する
---
8.1 条件分岐
8.1.1 if 式
Rustのifは式(expression)です:
fn main() {
let number = 3;
if number < 5 {
println!("条件は真");
} else {
println!("条件は偽");
}
}
if は式なので値を返せる:
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("number = {}", number); // 5
}
重要:各アームの型は同じでなければならない
// ❌ コンパイルエラー
let number = if condition { 5 } else { "six" };
// ✅ OK
let number = if condition { 5 } else { 6 };
8.1.2 else if
複数の条件を評価:
fn main() {
let number = 6;
if number % 4 == 0 {
println!("4で割り切れる");
} else if number % 3 == 0 {
println!("3で割り切れる");
} else if number % 2 == 0 {
println!("2で割り切れる");
} else {
println!("4、3、2のいずれでも割り切れない");
}
}
パフォーマンス:
- 条件は上から順に評価される
- 最初に真になった条件のみ実行
- 多くの条件がある場合は
matchの使用を検討
8.1.3 let if パターン
変数初期化での使用:
fn main() {
let is_even = true;
let result = if is_even {
"偶数"
} else {
"奇数"
};
println!("{}", result);
}
---
8.2 ループ
8.2.1 loop - 無限ループ
loopキーワードは無限ループを作成します:
fn main() {
let mut count = 0;
loop {
count += 1;
println!("count = {}", count);
if count == 5 {
break;
}
}
}
loopから値を返す:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("結果: {}", result); // 20
}
ループラベル:
fn main() {
let mut count = 0;
'outer: loop {
println!("count = {}", count);
let mut remaining = 10;
loop {
println!("remaining = {}", remaining);
if remaining == 9 {
break;
}
if count == 2 {
break 'outer;
}
remaining -= 1;
}
count += 1;
}
println!("終了: count = {}", count);
}
8.2.2 while - 条件付きループ
条件が真の間ループ:
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("発射!");
}
配列の走査(非推奨):
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("値: {}", a[index]);
index += 1;
}
}
問題点:
- インデックスミスでパニックの可能性
- 遅い(境界チェックが毎回必要)
8.2.3 for - コレクションのループ
最も安全で推奨される方法:
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("値: {}", element);
}
}
範囲を使う:
fn main() {
// 0..5 は 0, 1, 2, 3, 4
for number in 0..5 {
println!("{}", number);
}
// 0..=5 は 0, 1, 2, 3, 4, 5
for number in 0..=5 {
println!("{}", number);
}
// 逆順
for number in (1..=5).rev() {
println!("{}!", number);
}
println!("発射!");
}
イテレータメソッド:
fn main() {
let v = vec![1, 2, 3, 4, 5];
// インデックス付き
for (index, value) in v.iter().enumerate() {
println!("{}: {}", index, value);
}
// 可変参照
let mut v = vec![1, 2, 3];
for item in &mut v {
*item *= 2;
}
println!("{:?}", v); // [2, 4, 6]
}
---
8.3 match式
8.3.1 基本的なmatch
最も強力なパターンマッチング:
fn main() {
let number = 3;
match number {
1 => println!("一"),
2 => println!("二"),
3 => println!("三"),
_ => println!("その他"),
}
}
matchは式:
fn main() {
let number = 3;
let result = match number {
1 => "一",
2 => "二",
3 => "三",
_ => "その他",
};
println!("{}", result);
}
複数行の処理:
fn main() {
let number = 5;
match number {
1 => {
println!("一です");
println!("追加の処理");
}
2 | 3 => println!("二か三"),
4..=6 => println!("4から6"),
_ => println!("その他"),
}
}
8.3.2 パターンの種類
リテラル:
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
_ => println!("other"),
}
範囲:
let x = 5;
match x {
1..=5 => println!("1から5"),
6..=10 => println!("6から10"),
_ => println!("その他"),
}
複数パターン:
let x = 2;
match x {
1 | 2 => println!("1か2"),
3 | 4 => println!("3か4"),
_ => println!("その他"),
}
8.3.3 値の分配
タプル:
fn main() {
let pair = (0, -2);
match pair {
(0, y) => println!("x=0, y={}", y),
(x, 0) => println!("x={}, y=0", x),
_ => println!("どちらでもない"),
}
}
構造体:
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 0, y: 7 };
match point {
Point { x: 0, y } => println!("y軸上: y={}", y),
Point { x, y: 0 } => println!("x軸上: x={}", x),
Point { x, y } => println!("それ以外: ({}, {})", x, y),
}
}
列挙型:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::Move { x: 10, y: 20 };
match msg {
Message::Quit => println!("終了"),
Message::Move { x, y } => println!("移動: ({}, {})", x, y),
Message::Write(text) => println!("テキスト: {}", text),
Message::ChangeColor(r, g, b) => println!("色: ({}, {}, {})", r, g, b),
}
}
8.3.4 ガード
追加の条件を指定:
fn main() {
let number = 4;
match number {
x if x < 5 => println!("5未満: {}", x),
x if x % 2 == 0 => println!("偶数: {}", x),
x => println!("その他: {}", x),
}
}
複雑な条件:
fn main() {
let pair = (2, -2);
match pair {
(x, y) if x == y => println!("同じ値"),
(x, y) if x + y == 0 => println!("合計が0"),
(x, _) if x % 2 == 0 => println!("xは偶数"),
_ => println!("その他"),
}
}
8.3.5 @ バインディング
パターンマッチしながら値を束縛:
fn main() {
let number = 5;
match number {
n @ 1..=5 => println!("{}は1から5", n),
n @ 6..=10 => println!("{}は6から10", n),
_ => println!("その他"),
}
}
---
8.4 if let と while let
8.4.1 if let
単一のパターンマッチングを簡潔に:
fn main() {
let some_value = Some(3);
// match版
match some_value {
Some(3) => println!("three"),
_ => (),
}
// if let版(より簡潔)
if let Some(3) = some_value {
println!("three");
}
}
else節:
fn main() {
let some_value = Some(5);
if let Some(3) = some_value {
println!("three");
} else {
println!("not three");
}
}
8.4.2 while let
パターンマッチしながらループ:
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
// pop()がSome(value)を返す間ループ
while let Some(top) = stack.pop() {
println!("{}", top);
}
}
Vec のイテレーション:
fn main() {
let v = vec![1, 2, 3];
let mut iter = v.iter();
while let Some(value) = iter.next() {
println!("{}", value);
}
}
---
8.5 制御フローの比較
┌─────────────────────────────────────────────────────────┐
│ 制御構造の使い分け │
├─────────────────────────────────────────────────────────┤
│ │
│ if │
│ - 単純な条件分岐 │
│ - 2-3個の条件 │
│ │
│ match │
│ - 複数のパターンマッチ │
│ - 網羅性チェックが必要 │
│ - 列挙型の処理 │
│ │
│ if let │
│ - 単一パターンマッチ │
│ - Option/Resultの簡潔な処理 │
│ │
│ loop │
│ - 無限ループ │
│ - 明示的なbreak │
│ │
│ while │
│ - 条件付きループ │
│ │
│ for │
│ - コレクションの走査 │
│ - 範囲の反復 │
│ │
└─────────────────────────────────────────────────────────┘
---
8.6 実践例
例1:FizzBuzz
fn fizzbuzz(n: i32) {
for i in 1..=n {
match (i % 3, i % 5) {
(0, 0) => println!("FizzBuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
_ => println!("{}", i),
}
}
}
fn main() {
fizzbuzz(15);
}
例2:コマンドパーサー
enum Command {
Start,
Stop,
Restart,
Status,
}
fn parse_command(input: &str) -> Option<Command> {
match input.trim().to_lowercase().as_str() {
"start" => Some(Command::Start),
"stop" => Some(Command::Stop),
"restart" => Some(Command::Restart),
"status" => Some(Command::Status),
_ => None,
}
}
fn handle_command(cmd: Command) {
match cmd {
Command::Start => println!("サービスを開始します"),
Command::Stop => println!("サービスを停止します"),
Command::Restart => println!("サービスを再起動します"),
Command::Status => println!("サービスの状態を確認します"),
}
}
fn main() {
if let Some(cmd) = parse_command("start") {
handle_command(cmd);
} else {
println!("不明なコマンド");
}
}
例3:状態機械
enum State {
Idle,
Running,
Paused,
Stopped,
}
enum Event {
Start,
Pause,
Resume,
Stop,
}
fn next_state(state: State, event: Event) -> State {
match (state, event) {
(State::Idle, Event::Start) => State::Running,
(State::Running, Event::Pause) => State::Paused,
(State::Paused, Event::Resume) => State::Running,
(State::Running, Event::Stop) => State::Stopped,
(State::Paused, Event::Stop) => State::Stopped,
(s, _) => s, // その他は状態変化なし
}
}
fn main() {
let mut state = State::Idle;
state = next_state(state, Event::Start);
println!("実行中");
state = next_state(state, Event::Pause);
println!("一時停止");
state = next_state(state, Event::Resume);
println!("再開");
state = next_state(state, Event::Stop);
println!("停止");
}
例4:計算機
fn calculate(operator: char, a: f64, b: f64) -> Option<f64> {
match operator {
'+' => Some(a + b),
'-' => Some(a - b),
'*' => Some(a * b),
'/' if b != 0.0 => Some(a / b),
'/' => None, // ゼロ除算
_ => None, // 不明な演算子
}
}
fn main() {
let operations = vec![
('+', 5.0, 3.0),
('-', 5.0, 3.0),
('*', 5.0, 3.0),
('/', 5.0, 0.0),
];
for (op, a, b) in operations {
match calculate(op, a, b) {
Some(result) => println!("{} {} {} = {}", a, op, b, result),
None => println!("{} {} {} : エラー", a, op, b),
}
}
}
---
8.7 パフォーマンスの考慮
8.7.1 ループの最適化
// ❌ 遅い:境界チェックが毎回
let mut sum = 0;
let mut i = 0;
while i < arr.len() {
sum += arr[i];
i += 1;
}
// ✅ 速い:イテレータ使用
let sum: i32 = arr.iter().sum();
// ✅ 速い:forループ
let mut sum = 0;
for &item in &arr {
sum += item;
}
8.7.2 matchの最適化
コンパイラはmatchを最適化します:
// ジャンプテーブルに最適化される可能性
match value {
0 => handle_zero(),
1 => handle_one(),
2 => handle_two(),
// ...
_ => handle_default(),
}
---
8.8 まとめ
学んだこと
- ループ
- match式
- 簡潔な構文
次のステップ
次の章では、関数とクロージャを学びます:
- 関数定義と引数
- クロージャの構文
- 環境のキャプチャ
- イテレータとの組み合わせ
- The Rust Programming Language - Chapter 3.5
- The Rust Programming Language - Chapter 6
- Rust By Example - Flow of Control
---