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;
}
}
}
}乱数のために外部クレート rand を使います。Cargo.toml に追記するか、コマンドで追加します。
cargo add [email protected]登場した要素ダイジェスト
| コード | 意味 | 詳しくは |
|---|---|---|
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 ではキーボード入力ができないので、予想を配列で用意しました。
- 何回目で当たったかを数えて、正解時に「5 回で正解!」と表示してみよう
- 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;
}
}
}
}💡 解答例を見る
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;
}
}
}
}📝 まとめ
- 外部クレートは
cargo addで追加し、useで取り込む Resultはmatchやexpectで扱うmatchは全パターンを網羅しないとコンパイルが通らない
✅ 理解度クイズ
Q1
標準入力から 1 行読む
read_line(&mut guess) に &mut が付いている理由は?read_line は受け取った文字列に入力を 追記 します。中身を書き換えるには可変参照 &mut が必要です。参照については 05 章で詳しく学びます。
Q2
次のコードで
guess の型は最終的に何になる?let guess = String::from("42");
let guess: u32 = guess.trim().parse().expect("数字を入力して");
同じ名前で let し直す シャドーイング により、新しい guess は u32 型になります。parse() が返すのは Result ですが、expect で中身を取り出しています。
Q3
match guess.cmp(&secret) の各アームとして使われる値は?cmp は std::cmp::Ordering という enum を返します。match はすべてのバリアントを網羅しないとコンパイルエラーになるので、場合分けの漏れを防げます。