数当てゲームコンプリート

これまでザ・ブックの数当てゲームを読みながら、Rustの機能「標準ライブラリ」「変数」「型の関数」「標準入力」「参照」「パニック処理」「クレート」「トレイト」「matchフロー制御演算子」「シャドーイング」を紹介した。今回取り上げるのはloopキーワードだ。これで数当てゲームは完了である。

最初に答えを見てみよう。ザ・ブックに掲載されている数当てゲームをベースに多少書き方ものが次のソースコードだ。

数当てゲーム main.rs

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

fn main() {
    println!("数当てゲーム");

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

    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,
        };

        println!("入力値: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("小さすぎです!"),
            Ordering::Greater => println!("大きすぎです!"),
            Ordering::Equal => {
                println!("そのとおり!");
                break;
            }
        }
    }
}

このプログラムはユーザーからの入力を受け付け、最初に生成した数(1?100までの整数)を当てるまで繰り返しユーザーに入力を求める。前回から変更された点を以降で取り上げる。

loopキーワードとbreak

今回新しく追加されたのはloopキーワードだ。次のような使い方をするキーワードで、波括弧の間の処理を無限ループする目的で使われる。

loopキーワードで無限ループ

    loop {

    ...略...

    }

loopは動作としてはwhile trueに似ている。無限ループから抜ける時はbreakを使う。残りの処理を飛ばしてループの頭に戻る場合はcontinueだ。数当てゲームでは数が当たったら処理を終わらせたいので、次のように書き換えている。

数が当たったら無限ループを抜ける

    loop {

    ...略...

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("小さすぎです!"),
            Ordering::Greater => println!("大きすぎです!"),
            Ordering::Equal => {
                println!("そのとおり!");
                break;
            }
        }
    }

matchフロー制御演算子のところでOrdering::Equalだった場合に無限ループを抜けているわけだ。

loopキーワードとcontinue

これまでの数当てゲームでは、ユーザーが入力したデータが数字以外だった場合にエラーを起こしていたが、最終版では数字以外のデータが入力された場合に再度数字の入力を促すようになっている。ゲームとしてはこのほうが使いやすい。該当部分は次のとおりだ。

数字以外のデータの場合の処理をエラーからcontinueへ変更

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

シャドーイングしていた部分なのだが、まずここが「let guess: u32 = guess.trim().parse()」から「let guess: u32 = match guess.trim().parse()」に書き換わっている点に注目だ。matchフロー制御演算子はこんな感じでも使用できる。

そして、分岐対象がOk()とErr()になっており、Ok()だった場合は変換した数字を、Err()だった場合にはcontinueでループを飛ばして次のループが始まるといった仕組みになっている。

matchフロー制御演算子はとても強力な機能だ。Rustに特徴的な機能のひとつであり、ぜひとも慣れておきたい書き方である。

数当てゲーム完成版

完成した数当てゲームを実行すると次のようになる。小さな値と大きな値を入力して徐々に正解の値に近づけていき、数が当たるとプログラムは終了する。

  • 数当てゲーム完成版を実行例

    数当てゲーム完成版を実行例

ザ・ブックに掲載されている数当てゲームはソースコードとしては短いものだが、このソースコードにはRustの特徴がいい感じに詰まっている。これだけ短いソースコードだが、そこに盛り込まれている概念や機能は結構多い。

今回取り上げた機能も含めれば、数当てゲームで「標準ライブラリ」「変数」「型の関数」「標準入力」「参照」「パニック処理」「クレート」「トレイト」「matchフロー制御演算子」「シャドーイング」「loopーワード」に触れたことになる。このソースコードを理解するだけで、Rustの基本的な部分をかなり把握できるのだ。

Rustの強みはやはり堅さにあるように見える。厳密に書かないとコンパイラは通らない。かといってガチガチかといえば、実用性を考慮した書き方もできるようになっている。知れば知るほど魅力を感じるプログラミング言語だ。

Rustは人気シェアの上ではまだ1%未満(TIOBE Programming Community Index調べ)だが、そのシェアは徐々に増加している。プログラミング言語の難しさもあり、万人向けするようなものではないのだが、この堅さは小幅ながらも増加を続けるRustのシェアに実用言語としての期待が集まっているように見える。今のところRustの人気が陰る強い理由はなく、今後も同様の増加推移を見せる可能性がある。

参考資料