規模の大きい Rust プログラムを作る場合、モジュール の仕組みを使ってコードを階層化すると見通しがよくなります。
Rust でモジュールを定義するときは、mod
キーワードを使用します。
1 つの .rs
ファイルの中にインライン形式でモジュールを定義することも、別ファイルに分けてモジュールを定義することもできます。
クレートとモジュール
すべての Rust プログラムはクレート (crate) であり、バイナリクレート か ライブラリクレート のどちらかです。 クレートは、Rust におけるコンパイルの単位です。
- バイナリクレート … 1 つの実行ファイルを作り上げるもの。
- ライブラリクレート … 1 つのライブラリを作り上げるもの。
コンパイルはクレート単位で行われますが、コードレベルの実装では、モジュール という論理的な単位で分割/階層化できるようになっています。
クレートは 1 つ以上のモジュールで構成され、必ず 1 つの ルートモジュール(クレートルート) を持っています。
ルートモジュールのファイル名は決まっていて、バイナリクレートの場合は src/main.rs
で、ライブラリクレートの場合は src/lib.rs
です。
ルートモジュールはサブモジュールを含むことができ、さらに、サブモジュールも同様にサブモジュールを含むことができます。
上記のように、クレート内のモジュールはルートモジュールを起点とするツリー構造になるため、mod
キーワードを使ってモジュールを定義するとき、それは必ず何らかの親モジュールのサブモジュールということになります。
モジュールの作り方
インラインモジュール
mod
ブロックを使ってサブモジュールを定義することができます。
サブモジュールの実装をブロック内に直接記述するので、インラインモジュール と呼ばれます。
次の例では、calc
モジュールを定義して、その中に add
関数を定義しています。
モジュール内で定義した関数は、デフォルトでモジュール内からのみ参照可能 (private) になっているため、別モジュールから add
関数を呼び出せるようにするには pub
キーワードを付けて公開設定しておく必要があります(pub
の詳細は後述)。
親モジュール(ここではルートモジュール)から calc
モジュールの add
関数にアクセスするには、calc::add
というパスを使用します。
ファイルモジュール
サブモジュールを別ファイルとして実装することもできます。 というより、大きなプロジェクトを管理する場合、こちらのファイルを使ったサブモジュール化が主な使い方になると思います。
例えば、ルートモジュール (main.rs
) から呼び出すことのできる calc
サブモジュールを作るには、main.rs
と同じディレクトリに、calc.rs
あるいは calc/mod.rs
を配置します。
つまり、ディレクトリ構成は次のようになります。
別ファイルに分離した calc
モジュールのコードは次のようになります。
先ほど mod calc
ブロックの中に記述したコードと同じ内容です。
これをルートモジュールから利用するには、次のように mod calc;
というモジュール宣言を行うことで、src/calc.rs
(あるいは src/calc/mod.rs
)の内容を読み込むようコンパイラに指示します。
あとは、先ほどの例と同様、calc::add
という形でモジュール内の関数を呼び出せます。
サブサブモジュール
サブモジュール内でさらに mod
キーワードを使えば、ネストする形でサブモジュールを定義することができます(以下、サブサブモジュール)。
ここでは例として、sub
という名前のサブモジュールと、subsub
という名前のサブサブモジュールを作ることにします。
サブサブモジュールは、サブモジュールと同じ名前のディレクトリ名 (sub
) に .rs
ファイルを配置することで作成できます。
あるいは、mod.rs
を使う場合は次のような配置になります。
src
ディレクトリの一階層目をスッキリさせたいときは、こちらの配置の方がよいですね。
以下、それぞれの .rs
ファイルの実装例です。
pub
キーワードで公開設定しなければいけないのは、サブサブモジュールでも同様です(ここでは文字列定数 MY_NAME
を公開設定しています)。
デフォルトでは、サブモジュールには、その親モジュールからしかアクセスできないようになっています。
上記の例で言うと、ルートモジュール (main.rs
) からの sub
モジュールの参照や、sub
モジュールからの subsub
モジュールの参照は可能ですが、ルートモジュールから直接 subsub
モジュールを参照することはできません。
sub
モジュールの外から subsub
モジュールにアクセスできるようにするには、次のように pub mod
を使ってモジュールを公開設定します。
ここまで、pub
キーワードを付けて公開設定してきたのは、モジュール内のメンバー(関数や定数)でしたが、ネストされたモジュールの公開設定も同様の仕組みで行えるということですね。
下記のサンプルコードでは、ルートモジュール (main.rs
) から、サブサブモジュールに直接アクセスしています(sub::subsub
という階層化されたパスを使用します)。
ちなみに、sub::subsub::VALUE_3
と絶対パス指定している部分は、use
を使って次のように省略記述 (subsub::VALUE_3
) できるようになります。
公開範囲を制限する
ここまでの例では、シンプルな pub
指定による公開設定をしていましたが、これは、正確には 外部のクレートにまで公開する という意味になります。
外部クレートに公開するといっても、実際には、サブモジュールを pub
修飾しない限り、サブモジュール内のメンバーには外からはアクセスできないので、それだけで情報がダダ漏れになるということはありません。
とはいえ、公開範囲はできるだけ絞った方がよく、次のような追加のパス指定で、細かく公開範囲を制御できるようになっています。
指定方法 | 意味 |
---|---|
pub | 外部のクレートにまで公開 |
pub(crate) | カレントクレートに公開 |
pub(super) | 親モジュールに公開 (おすすめ) |
pub(super::super) | 親モジュールの親モジュールに公開 |
pub(in crate::my_mod) | 指定したパスのモジュールに公開(上位のモジュールのみを指定可能) |
(指定なし) | プライベート(モジュール内でのみ参照可能) |
pub(self) | 同上(モジュール内でのみ参照可能) |
上の指定方法の中でも使っていますが、モジュールパスは次のようなプレフィックスを組み合わせて指定することができます。
self::
… 自分自身のモジュールからの相対パスsuper::
… 親モジュールからの相対パスcrate::
… クレートルートからの絶対パス
クレート内でのみ使用する要素には、pub(crate)
以上の公開範囲(つまり pub
)を設定する必要はありません。
一般的に、モジュールの依存関係はツリー構造に従ったシンプルな構成になっていると保守しやすいので、まずは pub(super)
で公開設定できないかを考えてみるとよいです。