Rustのソースコードまとまり単位「クレート」

前回まで読んできたソースコードは次のとおり。数当てゲームの途中まで実装されたもので、このソースコードを通じて「標準ライブラリ」「変数」「型の関数」「標準入力」「参照」「パニック処理」について学んできた。短いソースコードだが、Rustの基本的な機能を示す優れたサンプルである。

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

use std::io;

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

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

    let mut guess = String::new();

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

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

ザ・ブックではこのソースコードに乱数の実装を追加する。Rustの標準ライブリには乱数の機能は含まれていないので、乱数の機能を使うためにザ・ブックではrandクレートを使っている。

そんなわけで今回は「クレート(crates)」について説明する。クレート(crates)は英単語としては「木枠」「木箱」といった意味で使われる言葉で、Rustではいわゆるライブラリに相当する概念として使われている。ある一定のソースコードの集まりがクレートと呼ばれており、クレートはRustのソフトウェアエコシステムにおいて重要な機能を果たしている。Rustではクレートの使用が推奨されており、そしてクレートベースの依存関係管理やバージョン管理などがよく機能している。

randクレートはライブラリクレートと呼ばれる種類のクレートで、いわゆるサードパーティ製のライブラリ指している。ライブラリクレートは次のサイトでホストされているものが使われることが多い。

  • crates.io: Rust Package Registry

    crates.io: Rust Package Registry

ザ・ブックの数当てゲームで使われているrandクレートは、次のページに掲載されている。

  • rand - crates.io: Rust Package Registry

    rand - crates.io: Rust Package Registry

執筆時点でのrandクレートのバージョンは0.7.3。バージョン0.7系は2019年7月にリリースされており、以降何度かマイナーバージョンアップが行われている。

これまでRustのビルドにはcargoコマンドを使っている。cargoコマンドにはクレートを扱う機能が実装されており、必要なクレートのダウンロードとビルド、依存しているクレートのダウンロードとビルド、必要に応じたマイナーアップデート、ビルド時の依存関係情報の保存などを行ってくれる。Linuxのパッケージ管理システムのRust内部版のようなことをしてくれるわけだ。

ちなみに数当てゲームも1つの「クレート」だ。main.rsを頂点とするソフトウェアのまとまりで、バイナリクレートと呼ばれる種類のクレートとなる。Rustではクレートがもっとも基本的なソフトウェアの単位であり、もっとも基本的な概念なので最初に覚えてしまおう。

使うライブラリクレートはCargo.tomlに書く

ザ・ブックや本連載の流れで作業を行っていれば、数当てゲームに含まれるCargo.tomlファイルは次のような内容になっていると思う。

自動生成されたままのCargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
authors = ["daich"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

使用するライブラリクレートはCargo.tomlに書く仕組みになっている。ここに利用したいライブラリクレートを書いておくと、あとはcargoコマンドがいい具合に処理してくれる。ザ・ブックではrandクレート version 0.5.5を使うということで次のように[dependenvies]セクションに「rand = "0.5.5"」という記述が追加されている。

rand 0.5.5を使うという指定を追加したCargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
authors = ["daich"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.5.5"

この状態で「cargo build」と実行すると、次のようにrandのダウンロードと依存するほかのクレートのダウンロード、およびこれらクレートのビルドが行われる。

randクレートとその依存クレートのダウンロードとビルド

> cargo build
    Updating crates.io index
   Compiling winapi v0.3.9
   Compiling rand_core v0.4.2
   Compiling rand_core v0.3.1
   Compiling rand v0.5.6
   Compiling guessing_game v0.1.0 (C:\Users\daich\Documents\rust_testbed\guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 11.09s
>
  • cargo buildの実行サンプル

    cargo buildの実行サンプル

rand 0.5.5を指定しているのに、上記の実行例ではrand 0.5.6がダウンロードされているが、これは問題ない。Cargo.tomlにおける0.5.5という表記はそもそもそういう指定だ。0.5.5と互換性がある最新版があればそちらが使われる。

Cargo.tomlというファイルの近くにCargo.lockというファイルがあることに気がついていると思うが、これはビルドに必要になるクレートとそのバージョンおよび依存関係をまとめたものだ。cargoはこのファイルに情報を書き出していくことで同じバージョンおよび依存関係でビルドできるようにしてくれている。

randクレートの指定を加える前のCargo.lockは次のような内容になっていたんではないかと思う。

自動生成されるCargo.lockファイル

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "guessing_game"
version = "0.1.0"

Cargo.lockにrand 0.5.5を追加してcargo buildを実行した後には、次のような内容に変わるはずだ。rand 0.5.5 (実際にはrand 0.5.6)を使用するために必要になる他のライブラリクレートとそのバージョンや依存関係が書いてあることがわかる。

rand 0.5.5 (0.5.6)を使うためのCargo.lock

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
 "bitflags",
]

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"

[[package]]
name = "guessing_game"
version = "0.1.0"
dependencies = [
 "rand",
]

[[package]]
name = "libc"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"

[[package]]
name = "rand"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
dependencies = [
 "cloudabi",
 "fuchsia-cprng",
 "libc",
 "rand_core 0.3.1",
 "winapi",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
 "rand_core 0.4.2",
]

[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"

[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

cargoという標準のビルドユーティリティがここまでライブラリの管理をしてくれるのは助かる。パッケージ管理システムとライブラリおよび依存関係の整理というのはパッケージを開発する側からすると常に頭の痛い問題だったのだが、RustだとそれがRust内部で完結してくれる。必要なデータはビルド時に最低限のものが勝手に用意されることになる。

マイナーアップデート

rand 0.5.5を指定してもrand 0.5.6がダウンロードされてきたわけだが、これはマイナーアップデートに当たる。マイナーアップデートについて自動的にアップデートが可能で、「cargo update」で実行できる。

  • cargo update

    cargo update

上記実行例はアップデート対象がないので何も行われていないが、マイナーアップデートが可能な場合にはここでマイナーアップデートが実施される。

メジャーアップデート

メジャーアップデートに相当する処理をする場合には、自分でバージョン番号を書き換える必要がある。例えば、次のように0.5.5のところを0.6.0へ書き換えてみよう。

randを0.5.5から0.6.0へ変更する

[package]
name = "guessing_game"
version = "0.1.0"
authors = ["daich"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.6.0"

この状態でcargo buildを実行すると、次のようにrand 0.6.5とそれが依存するクレートのダウンロードとビルドが行われる。

  • cargo buildで新しいバージョンのクレートに対するダウンロードとビルドが実施される

    cargo buildで新しいバージョンのクレートに対するダウンロードとビルドが実施される

当然、Cargo.lockの内容も次のように変わる。

rand 0.6.5を使う場合のCargo.lockファイル

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
 "bitflags",
]

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"

[[package]]
name = "guessing_game"
version = "0.1.0"
dependencies = [
 "rand",
]

[[package]]
name = "libc"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"

[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
 "autocfg",
 "libc",
 "rand_chacha",
 "rand_core 0.4.2",
 "rand_hc",
 "rand_isaac",
 "rand_jitter",
 "rand_os",
 "rand_pcg",
 "rand_xorshift",
 "winapi",
]

[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
 "autocfg",
 "rand_core 0.3.1",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
 "rand_core 0.4.2",
]

[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"

[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
dependencies = [
 "rand_core 0.3.1",
]

[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
 "rand_core 0.3.1",
]

[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
 "libc",
 "rand_core 0.4.2",
 "winapi",
]

[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
 "cloudabi",
 "fuchsia-cprng",
 "libc",
 "rand_core 0.4.2",
 "rdrand",
 "winapi",
]

[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
 "autocfg",
 "rand_core 0.4.2",
]

[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
 "rand_core 0.3.1",
]

[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
 "rand_core 0.3.1",
]

[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

本稿執筆時点でrandクレートの最新バージョンは0.7.3だったので、0.7系に変更して同じことをしてみよう。

さらにrandを0.7.0へ変更

[package]
name = "guessing_game"
version = "0.1.0"
authors = ["daich"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.7.0"
  • cargo buildでrand 0.7系へアップデート

    cargo buildでrand 0.7系へアップデート

rand 0.7.3を使う場合のCargo.lockファイル

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "getrandom"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
dependencies = [
 "cfg-if",
 "libc",
 "wasi",
]

[[package]]
name = "guessing_game"
version = "0.1.0"
dependencies = [
 "rand",
]

[[package]]
name = "libc"
version = "0.2.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"

[[package]]
name = "ppv-lite86"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"

[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
 "getrandom",
 "libc",
 "rand_chacha",
 "rand_core",
 "rand_hc",
]

[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
 "ppv-lite86",
 "rand_core",
]

[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
 "getrandom",
]

[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
 "rand_core",
]

[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

rand 0.6系とrand 0.7系とで依存するクレートの状況が変わっていることなどを確認できる。

マイナーアップデートに関しては不具合が発生する可能性は低いと見られるが、メジャーアップデートするとライブラリクレートの動作が変わるなどして不具合が発生する可能性もある。メジャーアップデートをする場合にはそのあたりも考慮して作業を行う必要がある。

最初からクレートを意識しよう

ビギナー向けのプログラミング言語ではサードパーティ製ライブラリやパッケージ、モジュールといった機能はかなりあとの方になってから習うか、基礎の段階ではそもそも習わないこともある。この点Rustはモダンなプログラミング言語だけあって一番最初からクレートの使用を前提としてザ・ブックが構成されている。よくできたドキュメントだ。

現在のソフトウェア開発ではオープンソースソフトウェアの活用はほぼ必須であり、モジュールやライブラリ、パッケージエコシステムの活用は欠かすことができない。こうした機能を学習の一番最初の段階で取り入れるのは実践的でよいことだ。教科書で学んだ方が実際の開発に挫折する理由のひとつに学習段階と開発段階での状況の開きというものがあるが、ザ・ブックでは最初から現場で実際に行う開発に近い方法が紹介され、その方法に沿って学習できるようになっている。

参考資料