Rustのモジュール
はじめに
本記事では、Rustのモジュールを理解するために以下のポイントを整理したいと思います。
-
use super::、crate::、self::とは何か -
pin_definitions::*のようなワイルドカードインポートの注意点 -
mod.rs・lib.rsの役割とRust 2018以降の変化 - PythonやC#など他言語との比較
1. crate, super, selfを使ったスコープ指定
Rustでは相対的・絶対的なパス指定が可能であり、crate, super, selfはその基点を示します。
crate:ルートモジュール
crate::はプロジェクトルート(main.rsまたはlib.rs)からの絶対パス参照を可能にします。
大規模プロジェクトで階層が深くなっても、crate::でルートから参照すればパス解決が明確になり、変更に強い設計が可能です。
super:親モジュール
super::は現在のモジュールから1階層上の親モジュールを指します。
superを使うと、兄弟モジュールへアクセスするときに自然な記述が可能です。
// pins/sensors.rs
use super::pin_definitions::*; // 親はpinsモジュールを指す
self:現在のモジュール自身
self::は「現在のモジュール」を指し示します。
明示的にself::を書くことで、「この参照はこのモジュール内を基点としている」ことをコード上で強調できます。
// features/mod.rs
mod subfeature;
pub fn call_subfeature() {
self::subfeature::do_something(); // 現在モジュール(features)直下のsubfeatureを参照
}
省略可能な場合も多いですが、コードを読む人にとって意図が明確になるため、プロジェクト内ルールや好みに応じて使い分けられます。
2. ワイルドカードインポートの注意点
use super::pin_definitions::*;のように*を使うと、モジュール内のpub要素をすべてインポートできます。この方法は手早い一方で、
-
名前衝突の可能性:
複数のワイルドカードインポートが衝突するケースが増えます。 -
可読性の低下:
どの要素がどこから来たか分かりにくくなります。
大規模プロジェクトや長期保守を考えると、必要な要素だけを{PinA, initialize_pin_a}のように明示的にインポートするのが望ましいです。
3. lib.rsとmod.rsの役割
lib.rs:ライブラリクレートのエントリーポイント
lib.rsはライブラリクレートのルートモジュールです。
ここでpub mod ...を書くことで、外部から利用できるAPIを整理します。
// src/lib.rs
pub mod features;
pub mod utils;
この設計により、他のプロジェクトはuse my_crate::features;のようにクレート名から直感的に参照できます。
mod.rs:かつてのフォルダ単位モジュール管理ファイル
かつては、mod.rsがフォルダをモジュールとして扱う際のエントリーポイントでした。
features/
├── mod.rs
├── submodule1.rs
└── submodule2.rs
mod.rs内でpub mod submodule1;と書くことでfeaturesモジュールとして機能しました。
Rust 2018以降:mod.rs不要化と新構造
Rust 2018以降、mod.rsは必須ではなく、以下のようにフォルダ名に対応する.rsファイルを使う方法が主流です。
src/
├── features.rs
└── features/
├── submodule1.rs
└── submodule2.rs
features.rsにpub mod submodule1;を書くだけでmod.rsと同じ機能を果たします。
これにより、特別なファイル名が不要になり、モジュール構造がより直感的になりました。
4. PythonやC#との比較
Pythonの__init__.pyとの類似点
Pythonは__init__.pyでフォルダをパッケージとして扱います。
Rustのmod.rsもこれに似ていますが相違点を簡単にまとめると
- Pythonは動的型付け言語で、インポートエラーが実行時に判明することも多い。
- Rustは静的解析でコンパイル時にエラーが分かる。
この点でRustはトラブルを早期に発見でき、信頼性が高いと言えます。
また、Python 3.3以降は__init__.py不要な名前空間パッケージも登場し、Rustも同様に2018以降mod.rs不要になり、両者「特別なファイル不要」の方向へ進化しています。
C#のpartial classとの違い
C#のpartial classは1つのクラス定義を複数ファイルに分割する仕組みで、GUI開発や自動生成コードで有用です。
Rustのモジュールは「ファイルやフォルダ構造」と「モジュール階層」が1対1で対応するため、クラス分割ではなく「モジュール分割」に焦点を当てています。
似たような「コード分割」のアイデアですが、扱う対象(クラス vs モジュール)が異なります。
Discussion