02. 数当てゲームで全体像

🎯 この章のゴール

  • 小さなゲームを作りながら、Rust の主要な要素を ざっと 眺める
  • 細かい理解は後の章に任せて「こんな感じか」を掴む

💡 ひとことで言うと

映画の予告編のような章です。変数・外部クレート・match・エラー処理・ループが一度に登場します。

完成形

use rand::RngExt;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("1〜100 の数を当ててね!");
    let secret = rand::rng().random_range(1..=100);

    loop {
        println!("予想を入力:");
        let mut guess = String::new();
        io::stdin()
            .read_line(&mut guess)
            .expect("読み込みに失敗しました");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue, // 数字以外は無視してやり直し
        };

        match guess.cmp(&secret) {
            Ordering::Less => println!("もっと大きい!"),
            Ordering::Greater => println!("もっと小さい!"),
            Ordering::Equal => {
                println!("正解!🎉");
                break;
            }
        }
    }
}
Playground で開く ↗

乱数のために外部クレート rand を使います。Cargo.toml に追記するか、コマンドで追加します。

登場した要素ダイジェスト

コード意味詳しくは
let mut guess変更可能な変数03 章
&mut guess可変参照を渡す05 章
.expect("...")失敗したら panic してメッセージ表示10 章
let guess: u32 = ...同名で再定義(シャドーイング)03 章
match値に応じて分岐、網羅性をチェック07 章
Ok(num) / Err(_)Result 型で成功・失敗を表す10 章
loop / break / continue無限ループと脱出03 章

Rust の変数は デフォルトで不変 です。書き換えたいときだけ mut を付けます。「どこで値が変わりうるか」がコードから一目でわかるようになります。

🧪 やってみよう

下の書きかけコードの TODO を埋めて、▶ 実行 で確かめよう。

Playground ではキーボード入力ができないので、予想を配列で用意しました。

  1. 何回目で当たったかを数えて、正解時に「5 回で正解!」と表示してみよう
  2. 1〜100 の範囲外の予想には注意メッセージを出してみよう
use std::cmp::Ordering;

fn main() {
    let secret = 42;
    let guesses = [50, 0, 25, 37, 43, 42]; // キーボード入力の代わり
    let mut count = 0;

    for guess in guesses {
        // TODO: 範囲外(1〜100 以外)なら注意して continue
        // TODO: count を増やす(範囲外の予想は数えない)
        match guess.cmp(&secret) {
            Ordering::Less => println!("{guess}: もっと大きい!"),
            Ordering::Greater => println!("{guess}: もっと小さい!"),
            Ordering::Equal => {
                println!("正解!🎉"); // TODO: 「N 回で正解!」にする
                break;
            }
        }
    }
}
Playground で開く ↗
💡 解答例を見る
use std::cmp::Ordering;

fn main() {
    let secret = 42;
    let guesses = [50, 0, 25, 37, 43, 42];
    let mut count = 0;

    for guess in guesses {
        if !(1..=100).contains(&guess) {
            println!("{guess}: 1〜100 の数で予想してね");
            continue;
        }
        count += 1;
        match guess.cmp(&secret) {
            Ordering::Less => println!("{guess}: もっと大きい!"),
            Ordering::Greater => println!("{guess}: もっと小さい!"),
            Ordering::Equal => {
                println!("{count} 回で正解!🎉");
                break;
            }
        }
    }
}
Playground で開く ↗

📝 まとめ

  • 外部クレートは cargo add で追加し、use で取り込む
  • Result は match や expect で扱う
  • match は全パターンを網羅しないとコンパイルが通らない

✅ 理解度クイズ

Q1
標準入力から 1 行読む read_line(&mut guess) に &mut が付いている理由は?
Q2
次のコードで guess の型は最終的に何になる?
let guess = String::from("42");
let guess: u32 = guess.trim().parse().expect("数字を入力して");
Q3
match guess.cmp(&secret) の各アームとして使われる値は?