rust: クレート、パッケージ、モジュール
概要
プログラムを複数のファイルに分割したときの取り扱い方について。
Cargoを使う前提とする。
参考
- 肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する - The Rust Programming Language 日本語版
- Cargoのワークスペース - The Rust Programming Language 日本語版
クレート(crate)
コンパイルは「クレート」が最小単位となっている。

バイナリクレートとライブラリクレートの種類が生まれるのはパッケージのようにも思うが、細かく区別する必要もないだろう。
バイナリクレートは cargo new --bin ( --bin はデフォルトなのでなくてもよい)で作られるタイプで src/main.rs がエントリーポイントと思っていて良いだろう。
ライブラリクレートは src/lib.rs を持つ。
cargo add はクレートを追加するManifestコマンドで、空のクレートを追加するのではなく Cargo.toml に依存関係を追加するコマンドである。
「Cargo.toml Manifestファイル」と書いてあるので、Manifestコマンドは Cargo.toml に関するコマンドだろう。
パッケージ(package)
「パッケージ」は1つ以上のクレートを持つ。
cargo new で作られるのはパッケージである。packageコマンドという分類になっている。

ライブラリクレートは最大でも1つなので src/lib.rs があるかどうかでわかる。
Cargo.toml では [lib] セクションでカスタマイズできる。
バイナリクレートも src/main.rs があるかどうかでわかるのだが、こちらは複数持つことができる。その場合は src/bin/ にディレクトリを作って main.rs を置く。
src/main.rs を持たずに src/bin/* に複数のディレクトリを作ってそれぞれに main.rs を持っても良い。
Cargo.toml では [[bin]] セクションでカスタマイズできる。括弧が1つ多い。
モジュール(module)
クレートやパッケージがどちらかといえば物理的?なのに対して、モジュールはどちらかといえば論理的なものだという印象を持った。
たぶん mod で定義できるからそう感じるのだろう。
mod <mod名> {...} という書き方は mod の定義と宣言を兼ねている。
mod <mod名>; とすると、このファイルで mod名 というモジュールを使用するという宣言になる。
mod の定義
同じファイルの中にあるので定義と使用する宣言を兼ねている。
use はC++の using と using namespace を混ぜたような使い方になる。
mod world_mod1 {
pub mod world_mod2 {
pub fn world_func() {
println!("abc world")
}
}
}
use world_mod1::world_mod2::world_func as w;
use world_mod1::world_mod2 as w2;
use world_mod1 as w3;
fn main() {
world_mod1::world_mod2::world_func();
w();
w2::world_func();
w3::world_mod2::world_func();
}
mod名 のファイルを作る
例えば hello_mod.rs というファイルを作り、その中に hello_func() という関数を作ったとする。
pub fn hello_func() {
println!("Hello, abc!");
}
それと同じディレクトリにある main.rs から hello_func() を呼び出したい場合はこうなる。
mod hello_mod;
fn main() {
hello_mod::hello_func();
}
つまり mod <ファイル名> {} で定義したのと同じ意味を持つことになる。
ディレクトリを作ってその下に mod名 のファイルを作る
同じディレクトリではなく別のディレクトリにしたい場合もある。
hello_func() を含んだファイルを hello_mod2.rs とする。
ディレクトリ hello_mod1 を作って hello_mod2.rs を移動させる。
そうすると、なんとなく main.rs からは mod hello_mod1::hello_mod2; と書けばよさそうな気がするが、そうは書けない。
mod に書くことができるのは単独のモジュール名だけで :: は使えないのだった。
その代わりに、 hello_mod1.rs というファイルを main.rs と同じディレクトリに作り、その中で pub mod hello_mod2; と書く。
以前は mod.rs というファイルに書く方式だったが今ではそうしなくてもよいそうだ。
テキストエディタのタブで “mod.rs” が複数表示されると面倒だ、みたいな理由らしいので特に理由がなければモジュールごとのファイル名を使えばよいだろう。
現在の方式に倣うとこうなる(はず。少なくともエラーにはならない)。
なんとなく main.rs にある mod はなくてもよいかと思ったのだがダメだった。
src/main.rs
mod hello_mod1;
use hello_mod1::hello_mod2;
fn main() {
hello_mod2::hello_func();
}
src/hello_mod1.rs
pub mod hello_mod2;
- src/hello_mod1/hello_mod2.rs
pub fn hello_func() {
println!("Hello, abc!");
}
モジュールツリー
モジュールは入れ子にできるので、モジュールツリーと呼ぶツリー構造として表すようになっている。
ツリーのルートは main.rs や lib.rs である。
use world_mod1::world_mod2 as w2; のように書いたあれは相対パスのような表現で、
これを絶対パスのような表現にすると use crate::world_mod1::world_mod2 as w2; とルートを表す crate:: が頭に付く。
そうなると、モジュールとして扱えるのは今のクレートの中だけということになるのでは?
もちろんそういうことはなく、外部で既に公開されている関数などを使うこともできる。
「パッケージ」なのだ。
とはいえ実行ファイルを取り込むこともできないだろうから、ライブラリクレートを含むパッケージのはずである。
パッケージには最大で 1つまでしかライブラリクレートを持つことができないので、ライブラリクレートを持つパッケージのみ use で使えるようにできるのだろう。

モジュール参照の絶対パスと相対パス
use でパスをスコープに持ち込んだり、関数呼び出しでパスを指定する際の書き方に、絶対パスと相対パスがある。
crate で始まるとルートからの絶対パスになる。
ファイルのパス指定と同じで、何も書かなければカレントモジュールから始まる。
self で初めても良い。ファイルで ./ を明示するかどうかに似ている。
self はパスの途中では使用できない。
super を使うと1つ上のパスになる。これはパスの途中で使うことができるが、最初が super で始まるときだけだ。
上がったり下がったりするような書き方はできないし、ルートである crate より上に上がることもできない。
なんというか、素直なパスの書き方だけと思っておけば良いだろう。
use にどこまで書くか
ルールではなく慣例があるそうだ。
- 関数はクレートまで
useしてコード中はクレート + 関数名()と書く structやenumなどは使いたい要素までuseしてコード中は要素だけ書く- 同じ要素名になる場合は関数と同じ書き方にする
- 慣例に従ったuseパスを作る
記載予定
- pub
- workspace