モジュールを別々のファイルに分ける
ここまで、この章のすべての例では、1つのファイルの中で複数のモジュールを定義してきました。 モジュールが大きくなってくると、コードをたどりやすくするために、それらの定義を別の ファイルに移したくなることがあります。
たとえば、複数の restaurant モジュールがあったリスト 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 ファイルを作成するまでは、これはコンパイルされません。
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
次に、波かっこ内にあったコードを、リスト 7-22 に示すように
src/front_of_house.rs という新しいファイルに置きます。コンパイラは、この
ファイルを探すべきことを知っています。というのも、クレートルートで
front_of_house という名前のモジュール宣言に出会っているからです。
pub mod hosting {
pub fn add_to_waitlist() {}
}
モジュールツリーの中で、ファイルを mod 宣言によって読み込む必要があるのは 一度だけ であることに注意してください。いったんコンパイラがそのファイルがプロジェクトの一部であることを知り(さらに、mod 文を置いた場所によって、そのコードがモジュールツリーのどこに属するのかも把握し)、プロジェクト内のほかのファイルは、「モジュールツリー内の項目を参照するためのパス」 節で説明したように、そのコードが宣言された場所へのパスを使って、読み込まれたファイルのコードを参照すべきです。言い換えると、mod は、ほかのプログラミング言語で見たことがあるかもしれない “include” 操作 ではありません。
次に、hosting モジュールを専用のファイルに切り出します。hosting はルート
モジュールではなく front_of_house の子モジュールなので、手順は少し異なります。
hosting 用のファイルは、モジュールツリー内のその祖先にちなんだ名前の新しい
ディレクトリに置きます。この場合は src/front_of_house です。
hosting の移動を始めるために、src/front_of_house.rs を、hosting
モジュールの宣言だけを含むように変更します。
pub mod hosting;
次に、src/front_of_house ディレクトリと hosting.rs ファイルを作成し、
hosting モジュール内で行われていた定義を含めます。
pub fn add_to_waitlist() {}
もし代わりに hosting.rs を src ディレクトリに置いた場合、コンパイラは
hosting.rs のコードがクレートルートで宣言された hosting モジュールに属すると
期待し、front_of_house モジュールの子として宣言されたものとは見なしません。
どのモジュールのコードについてどのファイルを調べるかというコンパイラの規則により、
ディレクトリとファイルはモジュールツリーにより近い形で対応することになります。
代替のファイルパス
ここまでは、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 では、1つのパッケージを複数のクレートに分割し、さらに1つのクレートをモジュールに
分割できるため、あるモジュールで定義された項目を別のモジュールから参照できます。
これを行うには、絶対パスまたは相対パスを指定します。これらのパスは use 文によって
スコープに持ち込めるので、そのスコープ内でその項目を何度も使うときに、より短いパスを
使えます。モジュールのコードはデフォルトでは非公開ですが、pub キーワードを追加する
ことで定義を公開できます。
次の章では、きちんと整理されたコードの中で使える、標準ライブラリのいくつかの コレクションデータ構造を見ていきます。