モジュールを複数のファイルに分割する

この章のすべての例において、今までのところ、複数のモジュールを一つのファイルに定義していました。 モジュールが大きくなる時、コードを読み進めやすくするため、それらの定義を別のファイルへ移動させたくなるかもしれません。

例として、複数のレストランモジュールを持つリスト7-17のコードからはじめましょう。 すべてのモジュールをクレートルートファイルで定義するのをやめて、複数のファイルにモジュールを抽出することにします。 今回、クレートルートファイルはsrc/lib.rsですが、この手続きはクレートルートファイルがsrc/main.rsであるバイナリクレートでもうまく行きます。

まず、front_of_houseモジュールをそれ専用のファイルに抽出しましょう。 front_of_houseモジュールの波かっこの中のコードを削除し、mod front_of_house;宣言だけを残して、 src/lib.rs がリスト7-21に示すコードを含むようにしてください。 リスト7-22で src/front_of_house.rs ファイルを作成するまで、このコードはコンパイルできないことに注意してください。

ファイル名: src/lib.rs

mod front_of_house;

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

リスト7-21: front_of_houseモジュールを宣言する。その中身はsrc/front_of_house.rs内にある

次に、リスト7-22に示すように、src/front_of_house.rs という名前の新しいファイルに、波かっこの中にあったコードを配置してください。 コンパイラは、クレートルートでfront_of_houseという名前のモジュール宣言を見つけたときは、このファイルを探せばいいということを知っています。

ファイル名: src/front_of_house.rs

pub mod hosting {
    pub fn add_to_waitlist() {}
}

リスト7-22: src/front_of_house.rsにおける、front_of_houseモジュール内部の定義

mod宣言を使用したファイルのロードは、モジュールツリー内で一回のみ行う必要があることに注意してください。 一度コンパイラが、そのファイルがプロジェクトの一部であることを認識したら(そして、mod文を書いた場所に応じてモジュールツリー内のどこにコードがあることになるかを認識したら)、「モジュールツリーの要素を示すためのパス」節で扱ったように、プロジェクト内の他のファイルは、モジュールが宣言された場所へのパスを使用してロードされたファイルのコードを参照するべきです。 別の言い方をすれば、modは他のプログラミング言語で見られるような“include”操作ではありません

つづけてhostingモジュールをそれ専用のファイルに抽出します。 hostingはルートモジュールの子ではなくfront_of_houseの子モジュールなので、このプロセスは少し異なります。 hostingのためのファイルを、モジュールツリー上での祖先に対応して名付けられた新しいディレクトリ(今回の場合は src/front_of_house/)内に配置しましょう。

hostingの移動を開始するために、src/front_of_house.rshostingモジュールの宣言のみを含むように変更します:

ファイル名: src/front_of_house.rs

pub mod hosting;

さらにsrc/front_of_house ディレクトリとhosting.rs ファイルを作って、hostingモジュール内でなされていた定義を持つようにします。

ファイル名: src/front_of_house/hosting.rs

pub fn add_to_waitlist() {}

もしsrcディレクトリ内にhosting.rsを置いた場合は、コンパイラはhosting.rsのコードはfront_of_houseモジュールの子としてではなく、クレートルート内で宣言されたhostingモジュールにあると期待するでしょう。 コンパイラがどのモジュールのコードのためにどのファイルをチェックするかの規則は、ディレクトリとファイルがモジュールツリーと密接に一致することを意味します。

別のファイルパス

ここまでRustコンパイラが使用するもっとも慣例的なファイルパスについて扱ってきましたが、Rustは古いスタイルのファイルパスもサポートしています。 クレートルート内で宣言されたfront_of_houseという名前のモジュールに対して、コンパイラは以下の場所からコードを探します:

  • src/front_of_house.rs (ここまで扱ってきたもの)
  • src/front_of_house/mod.rs (古いスタイルの、今もサポートされているパス)

front_of_houseのサブモジュールである、hostingという名前のモジュールに対しては、コンパイラは以下の場所からコードを探します:

  • src/front_of_house/hosting.rs (ここまで扱ってきたもの)
  • src/front_of_house/hosting/mod.rs (古いスタイルの、今もサポートされているパス)

同一のモジュールに対して両方のスタイルを使用するとコンパイルエラーになります。 異なるモジュールに対して両方のスタイルを混ぜて使用することは許可されていますが、プロジェクトを見て回る人たちにとって混乱を招くかもしれません。

mod.rs という名前のファイルを使用するスタイルの主な欠点は、プロジェクト内に mod.rs という名前のファイルが大量にできることになり、エディタで同時に開いたときに混乱を招きやすいことです。

各モジュールのコードを独立したファイルに移動しましたが、モジュールツリーは同じままです。 定義が別のファイルにあるにもかかわらず、eat_at_restaurant内での関数呼び出しもなんの変更もなくうまく行きます。 このテクニックのおかげで、モジュールが大きくなってきた段階で新しいファイルへ動かす、ということができます。

src/lib.rs におけるpub use crate::front_of_house::hosting という文も変わっていないし、useはどのファイルがクレートの一部としてコンパイルされるかになんの影響も与えないということに注意してください。 modキーワードがモジュールを宣言したなら、Rustはそのモジュールに挿入するためのコードを求めて、モジュールと同じ名前のファイルの中を探すというわけです。

まとめ

Rustでは、パッケージを複数のクレートに、そしてクレートを複数のモジュールに分割して、あるモジュールで定義された要素を他のモジュールから参照することができます。 これは絶対パスか相対パスを指定することで行なえます。 これらのパスはuse文でスコープに持ち込むことができ、こうすると、そのスコープで要素を複数回使う時に、より短いパスで済むようになります。 モジュールのコードは標準では非公開ですが、pubキーワードを追加することで定義を公開することができます。

次の章では、きちんと整理されたあなたのコードで使うことができる、標準ライブラリのいくつかのコレクションデータ構造を見ていきます。