randクレートを使うようにサンプルソースコードを変更

本連載でしばらく使っているサンプルソースコードは次のとおりだ。数当てゲームとしてすべての機能が実装されていないが、これまでこのソースコードから「標準ライブラリ」「変数」「型の関数」「標準入力」「参照」「パニック処理」について学んできた。

学習に使用している数当てゲームのソースコード

use std::io;

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

    println!("どの数だとおもう? = ");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("読み込み失敗");

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

前回は、このサンプルソースコードで乱数を使うために、「クレート」と呼ばれる標準機能以外のソフトウェアを導入する方法を学んだ。ターゲットとしたのは乱数を使うための「randクレート」だ。今回は、このクレートを使うように書き換えたソースコードを先に掲載する。

randクレートを使うように書き換えた数当てゲームのソースコード

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

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

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

    println!("シークレットナンバーはこれ: {}", secret_number);

    println!("どの数だとおもう? = ");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("読み込み失敗");

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

ビルドしてみよう(クレートのビルドも示したいので、一旦クリーンした状態でビルドをしてある)。

新しいサンプルソースコードのビルド

% cargo build
    Updating crates.io index
  Downloaded rand v0.7.3
  Downloaded getrandom v0.1.14
  Downloaded rand_core v0.5.1
  Downloaded rand_chacha v0.2.2
  Downloaded libc v0.2.74
  Downloaded cfg-if v0.1.10
  Downloaded ppv-lite86 v0.2.8
   Compiling libc v0.2.74
   Compiling getrandom v0.1.14
   Compiling cfg-if v0.1.10
   Compiling ppv-lite86 v0.2.8
   Compiling rand_core v0.5.1
   Compiling rand_chacha v0.2.2
   Compiling rand v0.7.3
   Compiling guessing_game v0.1.0 (/Users/daichi/Documents/rust_testbed/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 44.70s
% 

実行すると次のようになる。

新しいサンプルソースコードの実行の様子

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 26
どの数だとおもう? = 
10
入力値: 10

% 

乱数が生成されていることを確認できる。このサンプルで重要なのは、実行するごとに乱数の値が変わるということだ。例えば、続けて実行すると次のように生成される乱数が変わることを確認できる。

新しいサンプルソースコードの実行の様子(その2)

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 94
どの数だとおもう? = 
10
入力値: 10

% 

新しいサンプルソースコードの実行の様子(その3)

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 27
どの数だとおもう? = 
10
入力値: 10

% 

ここでのポイントは、クレートという標準ライブラリではないソフトウェアを導入し、その機能を利用しているということだ。どのようにクレートの機能を調べ、どのように実装する方法を調べればよいのか、そこが今回の学習のポイントとなる。

書き換えた内容をチェック

それほど多くないが、今回の変更点について見ていこう。

Rngトレイトを使用できるように追加

use rand::Rng;

上記のコードは、randクレートに含まれているRngトレイトを使うという意味になる。乱数ジェネレータで実装されているメソッドを使用するため、このトレイトをスコープに含めておく必要がある。トレイトはオブジェクト指向プログラミング言語で使われる言葉または概念で、完全に同じではないのだが似たようなポジションの言葉にインタフェースや抽象クラスというものがある。オブジェクト指向プログラミング言語によっては、トレイトではなくインタフェースや抽象クラスという言葉が使われており、今は似たようなものだと考えておいてもらえればと思う。

トレイト、インタフェース、抽象クラスという言葉が初見だという場合、乱数ジェネレータに含まれている機能を使うためにこれを書いておく必要がある、といった程度に考えておいていただきたい。

次にコードで実際に乱数を生成し、それを標準出力へ表示している。

乱数の生成と表示

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

    println!("シークレットナンバーはこれ: {}", secret_number);

rand::thread_rng()で現在のスレッドローカルな乱数ジェネレータを取得している。この乱数ジェネレータはこのスレッドにローカルなものであり、乱数のシードはオペレーティングシステムによって提供されている。多くのケースで実行するたびに別のシードが使われるので別の乱数が得られるはずだ。

gen_range(1, 101)で取得した乱数ジェネレータを使って乱数を生成している。このメソッドを呼び出すために、先程「use rand::Rng;」で使用するトレイトを宣言したわけだ。引数は1, 101となっており、この指定で1から100までの間で乱数を生成するようになる。

最後の行は生成した乱数を出力する処理だ。どのような乱数が生成されたのか確認するために追加されている。

クレートの使い方はドキュメントで得られる

ザ・ブックでは学習の初期段階で「クレート」を取り上げ、サードパーティ製のライブラリ(クレート)を使っていく方法を教えてくれる。これは、今後Rustを実用的な用途で使うプログラミング言語として活用していく上で重要だ。なぜなら、実世界にはサードパーティ製ライブラリの使い方をすべて解説した教科書は存在していないからである。

プログラミングの授業で使われる教科書には、サンプルソースコードとその説明が掲載されている。関数やメソッドの使い方が説明されており、説明を読めば実装できるようになっている。ただし、学校でプログラミング言語の授業の成績が良くても、それが必ずしも仕事に反映されるとは限らない。それは、現場で使用する関数やメソッドの多くが教科書には載っていないからだ。

関数やメソッドをどのように使うかを自分で調べていく必要があるし、そもそもどのようなライブラリ(クレート)が存在するのかを自分で調査し、評価し、選択しなければならない。それこそが技術力ということになってくる。学校の成績が良くてもプログラミングの出来がよくない場合、この辺りの架け橋ができていないところに理由あるように思う。

さて、Rustに話を戻すと、Rustではクレートの使い方をローカルでビルドして調べることができるようになっている。これが使い方を調べる1つの手段となる。

例えば、次のようにcargoコマンドを使うと、今実装しているソースコードに関連しているすべてのクレートのマニュアルをビルドすることができる。

cargo docでプロジェクトに関連するクレートのマニュアルをビルド

% cargo doc
   Compiling libc v0.2.74
   Compiling getrandom v0.1.14
    Checking cfg-if v0.1.10
    Checking ppv-lite86 v0.2.8
 Documenting cfg-if v0.1.10
 Documenting ppv-lite86 v0.2.8
 Documenting libc v0.2.74
    Checking rand_core v0.5.1
    Checking rand_chacha v0.2.2
    Checking rand v0.7.3
 Documenting getrandom v0.1.14
 Documenting rand_core v0.5.1
 Documenting rand_chacha v0.2.2
 Documenting rand v0.7.3
 Documenting guessing_game v0.1.0 (/Users/daichi/Documents/rust_testbed/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 11.46s
 % 

cargo docが終わると、次のようにtarget/doc/以下にすべてのドキュメントが生成される。

target/doc/以下にすべてのドキュメントが生成される

% tree -L 1 target/doc/
target/doc/
├── aliases.js
├── brush.svg
├── cfg_if
├── COPYRIGHT.txt
├── dark.css
├── down-arrow.svg
├── favicon.ico
├── FiraSans-LICENSE.txt
├── FiraSans-Medium.woff
├── FiraSans-Regular.woff
├── getrandom
├── guessing_game
├── implementors
├── libc
├── LICENSE-APACHE.txt
├── LICENSE-MIT.txt
├── light.css
├── main.js
├── normalize.css
├── noscript.css
├── ppv_lite86
├── rand
├── rand_chacha
├── rand_core
├── rust-logo.png
├── rustdoc.css
├── search-index.js
├── settings.css
├── settings.html
├── settings.js
├── source-files.js
├── source-script.js
├── SourceCodePro-LICENSE.txt
├── SourceCodePro-Regular.woff
├── SourceCodePro-Semibold.woff
├── SourceSerifPro-Bold.ttf.woff
├── SourceSerifPro-It.ttf.woff
├── SourceSerifPro-LICENSE.md
├── SourceSerifPro-Regular.ttf.woff
├── src
├── storage.js
├── theme.js
└── wheel.svg

10 directories, 33 files
% 

randクレートのドキュメントはtarget/doc/rand/以下に生成される。

randクレートのドキュメントはtarget/doc/rand/以下に生成される

% tree -L 1 target/doc/rand
target/doc/rand
├── all.html
├── distributions
├── fn.random.html
├── fn.thread_rng.html
├── index.html
├── prelude
├── rngs
├── seq
├── sidebar-items.js
├── struct.Error.html
├── trait.AsByteSliceMut.html
├── trait.CryptoRng.html
├── trait.Rng.html
├── trait.RngCore.html
└── trait.SeedableRng.html

4 directories, 11 files
% 

target/doc/rand/index.htmlをWebブラウザで開けば、randクレートのマニュアルを閲覧することができる。次のような感じだ。

  • randクレートのドキュメント

    randクレートのドキュメント

上記のページを見るとわかると、トレイトのところにRngが存在していることがわかる。このリンクをクリックすると次のようなページが表示される。

  • Rngトレイトのドキュメント

    Rngトレイトのドキュメント

Rngトレイトのドキュメントを見ていくと、次のようにgen_range()の説明とサンプルコードを読むことができる。

  • gen_range()メソッドの説明とサンプル

    gen_range()メソッドの説明とサンプル

同じドキュメントは、先日紹介したクレートのポータルサイトでも読むことができる。

randクレートのドキュメントなら上ポータルサイトから調べて「rand - Rust」で読むことができる。実は、randクレートはビルドして生成されるドキュメント以外にも導入用ドキュメントが用意されており(「Introduction - The Rust Rand Book」)、ライブラリクレートの導入としてはかなり取り掛かりやすい対象ではある。

こんな感じでRustではすでにサードパーティ製ライブラリ(ライブラリクレート)を得るためのエコシステムやドキュメント体制が整っているので、あとは本人のやる気次第だ。欲しいライブラリクレートを探し、ドキュメントを読み、サンプルコードを書いたり使ったりして評価してみる。希望のものが見つかればそのクレートやトレイトの説明をよく読んで理解を深めていく、といったことを行う。

学習の初期段階でこうした部分に触れているのが、ザ・ブックの秀逸なところだ。最終的にはこのようにしてサードパーティ製のライブラリクレートを活用することになるので、この手順は早めに覚えてしまったほうがよいと思う。

参考資料