コンテンツにスキップ

[The book] 2. Programming a Guessing Game

Rust The book の第2章 Programming a Guessing Game

プロジェクトの作成

$ cargo new guessing_game
# 実行確認
$ cd guessing_game
$ cargo run

推測値を入力させる

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    // Mutableな変数guessを作成し、作成したStringのインスタンス(UTF-8の文字列)を束縛する
    // String::newはStringのstaticメソッド (not constructor)
    // 大抵のTypeは`::new`というstaticメソッドを持つ
    let mut guess = String::new();

    io::stdin() // 標準入力から..
        .read_line(&mut guess) // 書き換え可能なbufferの参照に入力された値を入れたResultを取得
        .expect("Failed to read line"); // 失敗した場合はメッセージを表示して止める (この行はなくても警告は出るが動く)

    println!("You guessed: {}", guess);
}

乱数createとcreateの扱い方

rand createを使う。

Cargo.toml[dependencies]配下に追加する。
IntelliJ IDEAだとバージョンは補完できる。

[dependencies]
rand = "0.7.3"

バージョンはSemantic Versioningが採用されている。
0.7.3は固定バージョンではなく、^0.7.3の省略形である。

cargo buildcargo runを実行すると、createがダウンロード/コンパイルされる。

Cargo.lockファイルにインストールしたバージョンは記載されている。
cargo buildの際は、そのバージョンのcreateを使う。(dependenciesの要件を満たしていれば)

dependenciesを満たす最新のcreateが必要ならcargo updateを実行する。
Cargo.lockファイルを無視して条件を満たす最新のcreateを取得する。

乱数を作る

use rand::Rng;
use std::io;

fn main() {
    println!("Guess the number!");

    // 1から100(101-1)の範囲で乱数を作成しsecret_numberに束縛する
    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}

乱数を比較する

guessはStringに推論されるため、そのままでは比較できない。
文字列型を数値型に変換するため以下の処理を追加する。

    // trimはユーザ入力の改行を除去するために必要
    // : u32 で parseの変換後に期待する型がu32であることを示している
    let guess: u32 = guess.trim().parse().expect("Please type a number!");

guessは既に使用されている変数だがRustではこれが許される。(Shadowing)
型変換で再束縛するケースを想定して同名変数の再利用を許可しているからだ。

チャレンジを複数回行う

loop { }で対象処理を無限ループする。

// --snip--

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = guess.trim().parse().expect("Please type a number!");

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => println!("You win!"),
        }
    }

// --snip--

正解の場合にゲームを終了する

breakを差し込む。

          Ordering::Equal => {
              println!("You win!");
              break;
          }

不正な入力の例外処理

guessの変換にmatch式を用いる。

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            // _ はすべて(なんでもOK)の意味
            Err(_) => continue,
        };

最終調整

secret_numberを非表示にして完成。

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

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}