Rustもようやく多少は書き慣れてきたので、今の段階でわかっているパッケージやモジュールのあれこれをまとめます。
参考ページ
Managing Growing Projects with Packages, Crates, and Modules - The Rust Programming Language
相関図
パッケージ・クレート・モジュールを簡単な図にすると、次のようになります。

クレートは「~.rs」ファイルのことです。
クレート内にモジュールを定義し、コードをブロックとしてまとめることもできます。
cargoを使って新しいプロジェクトを作成します。
$ cargo new my-project Created binary (application) `my-project` package $ cd my-project/ $ ls Cargo.lock Cargo.toml src
[package]
name = "my-project"
version = "0.1.0"
edition = "2021"
パッケージとして「my-project」が作成されました。
クレート
$ tree . . ├── Cargo.toml └── src └── main.rs 1 directory, 2 files
特にオプションを付けずに「cargo new」コマンドを実行すると、「main.rs」が作成されます。
これがバイナリクレートで、ここ記されたコードがデフォルトで「cargo build」コマンドによりコンパイルされ、バイナリが作成されます。
またバイナリでないクレートを作るには、src内に「lib.rs」を手動で作成するか、「cargo new」コマンドに、オプション「--lib」をつけて実行します。
このファイルがライブラリのルート(起点)となるクレートになります。
$ tree src src ├── lib.rs └── main.rs 0 directories, 2 files
pub fn root_func() {}
fn main() {
my_project::root_func();
}
lib.rsの機能をmainに持ち込むには、パッケージ名※を使います。
パッケージ名::関数名
※今回のパッケージは「my-project」にしましたが、呼び出す際にハイフンは使えないため、アンダースコアになっていて「my_project」で呼び出します。
モジュール
キーワード「mod」を使ってできることは2つあります。
- モジュールの定義
- モジュールの取り込み
モジュールの定義
クレート内で「mod」を使ってモジュールを定義すると、コードをひとまとまりのブロックとして分けることができます。
pub fn root_func() {
println!("Root");
}
pub mod branch {
pub fn branch_func() {
println!("Root / Branch");
}
}
fn main() {
my_project::root_func();
my_project::branch::branch_func();
}
mainでは、パッケージ名::モジュール名::関数名のようにして呼び出し可能です。
また、モジュール内でさらにモジュールの定義でき、
pub mod branch {
pub fn branch_func() {
println!("Root / Branch");
}
pub mod leaf {
pub fn leaf_func() {
println!("Root / Branch / Leaf");
}
}
}
use my_project::branch;
fn main() {
my_project::root_func();
branch::branch_func();
branch::leaf::leaf_func();
// 出力
// Root
// Root / Branch
// Root / Branch / Leaf
}
このように呼び出すことができます。
lib.rsから内側にあるモジュールの機能を呼びたいなら、呼び出すところから見て絶対パスのcrateやself、モジュール名からたどっていて記述し、モジュールから参照したい場合には、同じくcrate、selfのほか、superなどを使って呼び出します。
モジュールの取り込み
「mod」キーワードでできることのもうひとつがモジュールの取り込みで、クレートやモジュールが肥大化してきたときに有効です。
前の項目のbranchモジュールの内容を「src/branch.rs」を作成して移動します。
pub fn branch_func() {
println!("Root / Branch");
}
pub mod leaf {
pub fn leaf_func() {
println!("Root / Branch / Leaf");
}
}
mod branch{}で囲っていたところを除き、ネストを減らした形です。
そして「lib.rs」に一文を加えます。
pub mod branch;
pub fn root_func() {
println!("Root");
}
先程のブロックとして定義していた{}ではなく、;を使ってbranch.rsの内容を取り込んでいます。
こうすることで、「lib.rs」内で「branch.rs」の内容を定義しているのと同じことになります。
なので呼び出しは、
use my_project::branch;
fn main() {
my_project::root_func();
branch::branch_func();
branch::leaf::leaf_func();
}
先程と同じ記述で動作します。
ライブラリクレートを経由せず、「main.rs」に直接取り込むことも可能です。
mod branch;
fn main() {
my_project::root_func();
crate::branch::branch_func();
self::branch::leaf::leaf_func();
}
crateやselfを使って統一していないのは例示するためで、branchから書いても問題ありません。