はじめに
Rust プログラムが大きくなると、すべての型と関数を 1 つのファイルに置いたままでは、それぞれの役割を把握しにくくなります。CLI の実験では、すでにライブラリとバイナリを確認し、テストの実験ではテストモジュールを確認しました。この実験では、これらの準備済みの例を 1 つのモデルに結び付けます。パッケージは Cargo がビルドするプロジェクト、クレートはコンパイル単位、モジュールはクレート内の名前を整理する仕組みです。
ここでは、小さな pantry-report パッケージを整理します。ファイルと実装コードの大部分は準備済みなので、各ステップでは、ライブラリクレートからの共有、ファイルモジュールの宣言、バイナリクレートへの小さな public API のインポートという、1 つの境界に集中します。
クレート間で関数を共有する
このステップでは、パッケージに含まれる 2 つのクレートを確認し、ライブラリの関数をバイナリから利用できるようにします。
準備済みの Cargo パッケージへ移動します。
cd /home/labex/project/pantry-report
Cargo.toml には、pantry-report という名前の 1 つのパッケージが記述されています。Cargo は src/lib.rs をライブラリクレートのルート、src/main.rs をバイナリクレートのルートとして認識します。両者は同じパッケージに属しますが、別々のクレートとしてコンパイルされます。
短いクレートルートを確認します。sed -n コマンドはファイルを編集せず、指定した行範囲だけを表示します。
sed -n '1,120p' src/lib.rs
sed -n '1,120p' src/main.rs
バイナリは、ライブラリクレートのパスを通して report_title をインポートしています。ハイフンを含むパッケージ名は、Rust のソースコード内ではアンダースコアを含むクレート名になります。そのため、pantry-report は pantry_report になります。
準備済みの戻り値の型 &'static str は、プログラム全体で有効な借用された文字列リテラルを表します。'static はライフタイム注釈です。明示的なライフタイムの規則はこの初級コースの範囲外なので、この実験で学習者が編集する内容は、ライフタイムの理解や記述に依存しません。
アイテムは pub を付けない限り、そのモジュール内からのみ利用できます。ライブラリのルートを開きます。
nano src/lib.rs
関数宣言だけを次のように変更します。
fn report_title() -> &'static str {
次の内容に変更してください。
pub fn report_title() -> &'static str {
pub を付けると、この関数がライブラリクレートの public インターフェースの一部になります。Ctrl+O で保存し、Enter を押してから、Ctrl+X で終了します。
cargo check を使って、最終的な実行可能ビルドを生成せずに両方のクレートを型チェックします。
cargo check
続いて、バイナリクレートを実行します。
cargo run --quiet
出力は次のようになります。
Pantry Report
この出力により、バイナリがクレート境界を越えて、ライブラリの public 関数を呼び出せたことを確認できます。
ファイルモジュールを宣言する
このステップでは、準備済みの inventory.rs ファイルをライブラリクレートのモジュールツリーに接続します。
Rust のソースファイルは、存在するだけではコンパイルされません。クレートのルートでモジュールを宣言する必要があります。inventory という名前で宣言すると、Rust は src/inventory.rs を探し、その中のアイテムを inventory::... というパスの下に配置します。
準備済みのモジュールファイルを確認します。先ほどと同じように、sed -n '1,200p' は -n によって自動出力を抑制し、1,200p によって指定した行範囲だけを表示します。
sed -n '1,200p' src/inventory.rs
#[derive(Debug)] の行は、Item に標準のデバッグフォーマット機能を生成するよう Rust に指示します。表示するプログラムはこの機能に依存していないため、これは新しい要件ではなく、準備済みのメタデータとして扱ってください。Item 型、そのフィールド、describe は public です。ヘルパー関数 availability には pub がないため、モジュール内でのみ利用できます。describe はこの private なヘルパーを呼び出せますが、モジュール外の呼び出し元が受け取れるのは public な結果だけです。
ライブラリのルートを開きます。
nano src/lib.rs
// MODULE_DECLARATION を次の内容に置き換えます。
pub mod inventory;
最初の pub は、ライブラリクレートを通してモジュールを公開します。mod inventory; の部分は、ファイルをモジュールツリーに接続します。Nano で保存して終了したら、パッケージをチェックします。
cargo check
Finished `dev` profile ...
チェックが成功すれば、Rust が src/inventory.rs を見つけ、pantry_report::inventory としてコンパイルしたことを確認できます。
public API をインポートして使用する
このステップでは、use 宣言を使ってモジュールのアイテムをバイナリクレートに取り込み、食料品の記録を表示します。
インポートしない場合、完全なパスは pantry_report::inventory::Item と pantry_report::inventory::describe です。use 宣言を使うと、アイテムを移動したりコピーしたりせずに、現在のスコープで短い名前を使用できます。
バイナリのソースファイルを開きます。
nano src/main.rs
// INVENTORY_IMPORT を、次のグループ化されたインポートに置き換えます。
use pantry_report::inventory::{describe, Item};
波括弧を使うと、同じパス接頭辞を持つ 2 つのアイテムをまとめて指定できます。次に、// INVENTORY_REPORT を次の内容に置き換えます。
let lentils = Item {
name: String::from("lentils"),
quantity: 4,
};
println!("{}", describe(&lentils));
型と両方のフィールドが public なので、バイナリは Item を生成できます。describe には Item を借用して渡します。public なこの関数は、内部で同じモジュールにある private な availability を呼び出します。
Nano で保存して終了したら、パッケージを実行します。
cargo run --quiet
Pantry Report
lentils: 4 jars (stocked)
この 2 行の出力から、クレートレベルの共有とモジュールレベルの整理が、小さな public API を通して機能していることが分かります。
まとめ
1 つの Cargo パッケージに含まれるライブラリクレートとバイナリクレートを操作し、ライブラリのアイテムを public にしました。また、ファイルモジュールを宣言し、クレートとモジュールのパスを確認し、use でパスを短縮し、小さな public API の背後に実装用ヘルパーを private のまま保持しました。


