Rustのモジュール
はじめに
本記事では、Rustのモジュールを理解するために以下のポイントを整理したいと思います。
-
use super::
、crate::
、self::
とは何か -
pin_definitions::*
のようなワイルドカードインポートの注意点 -
mod.rs
・lib.rs
の役割とRust 2018以降の変化 - PythonやC#など他言語との比較
crate
, super
, self
を使ったスコープ指定
1. 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}
のように明示的にインポートするのが望ましいです。
lib.rs
とmod.rs
の役割
3.
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
モジュールとして機能しました。
mod.rs
不要化と新構造
Rust 2018以降:Rust 2018以降、mod.rs
は必須ではなく、以下のようにフォルダ名に対応する.rs
ファイルを使う方法が主流です。
src/
├── features.rs
└── features/
├── submodule1.rs
└── submodule2.rs
features.rs
にpub mod submodule1;
を書くだけでmod.rs
と同じ機能を果たします。
これにより、特別なファイル名が不要になり、モジュール構造がより直感的になりました。
4. PythonやC#との比較
__init__.py
との類似点
PythonのPythonは__init__.py
でフォルダをパッケージとして扱います。
Rustのmod.rs
もこれに似ていますが相違点を簡単にまとめると
- Pythonは動的型付け言語で、インポートエラーが実行時に判明することも多い。
- Rustは静的解析でコンパイル時にエラーが分かる。
この点でRustはトラブルを早期に発見でき、信頼性が高いと言えます。
また、Python 3.3以降は__init__.py
不要な名前空間パッケージも登場し、Rustも同様に2018以降mod.rs不要になり、両者「特別なファイル不要」の方向へ進化しています。
partial class
との違い
C#のC#のpartial class
は1つのクラス定義を複数ファイルに分割する仕組みで、GUI開発や自動生成コードで有用です。
Rustのモジュールは「ファイルやフォルダ構造」と「モジュール階層」が1対1で対応するため、クラス分割ではなく「モジュール分割」に焦点を当てています。
似たような「コード分割」のアイデアですが、扱う対象(クラス vs モジュール)が異なります。
Discussion