🔖

Rustのモジュール

2024/12/09に公開

はじめに

本記事では、Rustのモジュールを理解するために以下のポイントを整理したいと思います。

  • use super::crate::self::とは何か
  • pin_definitions::*のようなワイルドカードインポートの注意点
  • mod.rslib.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.rsmod.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.rspub 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