Rustのモジュール
ここまでの例は、すべてひとつの main.rs ファイルに収まっていました。小さなプログラムならそれで十分ですが、実際のプロジェクトではコードを意味のあるまとまりに分けていきます。そのための Rust の仕組みがモジュールシステム、つまり mod、pub、use です。
モジュールを定義する
Section titled “モジュールを定義する”モジュール(module)とは、関連するコードをひとまとめにした、名前付きのブロックです。
mod math { pub fn add(a: i32, b: i32) -> i32 { a + b }}
fn main() { let sum = math::add(2, 3); println!("{sum}");}実行結果:
5ポイントは2つです。まず、関数はモジュール名をパスとして使い、math::add(...) のように呼び出します。そして、関数に pub が付いています。pub がないと、モジュールの外からは呼び出せません。
何もしなければすべて非公開
Section titled “何もしなければすべて非公開”意外に思う人が多いので、ここははっきり書いておきます。モジュールの中身はデフォルトで非公開(private)です。上の例から pub を外すと、コンパイルが通らなくなります。
mod math { fn add(a: i32, b: i32) -> i32 { // `pub` がない a + b }}
fn main() { let sum = math::add(2, 3); // ❌ エラー:関数 `add` は非公開 println!("{sum}");}モジュールの外から使ってほしいものには、pub を明示的に付ける必要があります。これはわざとそうなっています。モジュールの内部だけで使う補助関数は隠しておき、公開 API として見せたい部分だけを外に出せるようにするためです。
モジュールの入れ子
Section titled “モジュールの入れ子”モジュールの中に、さらにモジュールを入れることもできます。大きなプロジェクトは、こうやって木のような構造を作っていきます。
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
fn main() { let area = shapes::circle::area(2.0); println!("{area}");}use:長いパスを短くする
Section titled “use:長いパスを短くする”毎回 shapes::circle::area(...) と書くのは面倒です。use を使うとパスをスコープに持ち込めるので、短い名前で直接呼べるようになります。
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
use shapes::circle;
fn main() { let area = circle::area(2.0); // shapes::circle::area より短い println!("{area}");}じつは、use はもう使ったことがあります。HashMap のページに出てきた use std::collections::HashMap; と同じキーワードです。
モジュールをファイルに分ける
Section titled “モジュールをファイルに分ける”上の例ではすべてをひとつのファイルに書きました。Rust Playground ではそうするしかないからです。自分のパソコンで作る実際のプロジェクトでは、モジュールごとにファイルを分けるのがふつうです。たとえば math モジュールがあるプロジェクトなら、次のような構成になります。
src/├── main.rs└── math.rsmod math; // Rust に「このモジュールのコードは math.rs にある」と伝える
fn main() { let sum = math::add(2, 3); println!("{sum}");}pub fn add(a: i32, b: i32) -> i32 { a + b}mod math;(中身を書かず、セミコロンで終わる形)と書くと、Rust は同じディレクトリにある math.rs からモジュールの中身を読み込みます。プロジェクトがひとつのファイルに収まらなくなったら、すぐにこの書き方を使うことになります。
1. 温度変換モジュール
Section titled “1. 温度変換モジュール”convert というモジュールを作り、2つの公開関数を持たせてください。c_to_f(摂氏から華氏へ)と f_to_c(その逆)です。main から 100°Cは212°Fです と 212°Fは100°Cです を表示します。
ヒント:公式は c * 9.0 / 5.0 + 32.0 と (f - 32.0) * 5.0 / 9.0 です。pub を忘れずに。関数は convert::c_to_f(100.0) のように、モジュール名を付けて呼び出します。
解答を見る
mod convert { pub fn c_to_f(c: f64) -> f64 { c * 9.0 / 5.0 + 32.0 }
pub fn f_to_c(f: f64) -> f64 { (f - 32.0) * 5.0 / 9.0 }}
fn main() { println!("100°Cは{}°Fです", convert::c_to_f(100.0)); println!("212°Fは{}°Cです", convert::f_to_c(212.0));}実行結果:
100°Cは212°Fです212°Fは100°Cですどちらの関数にも pub が付いているので、モジュールの外にある main から呼び出せます。どちらかの pub を消すと、その関数は非公開だとコンパイラが教えてくれます。
2. 非公開のヘルパー関数
Section titled “2. 非公開のヘルパー関数”入れ子のモジュール school::grades を作り、公開関数 letter(score: u32) -> char を持たせてください。90以上なら 'A'、80以上なら 'B'、それ以外なら 'C' を返します。grades の中には、letter が使う非公開のヘルパー関数 is_at_least(score: u32, min: u32) -> bool も書きます。use で school::grades をスコープに持ち込み、95、85、70の評価を表示してください。
ヒント:school の中の mod grades と fn letter にはどちらも pub が必要ですが、is_at_least には付けません。同じモジュールの中のコードなら、非公開の関数もいつでも呼び出せます。
解答を見る
mod school { pub mod grades { pub fn letter(score: u32) -> char { if is_at_least(score, 90) { 'A' } else if is_at_least(score, 80) { 'B' } else { 'C' } }
// 非公開:`grades` の中のコードからしか呼べない fn is_at_least(score: u32, min: u32) -> bool { score >= min } }}
use school::grades;
fn main() { for score in [95, 85, 70] { println!("{score} -> {}", grades::letter(score)); }}実行結果:
95 -> A85 -> B70 -> Cletter が is_at_least を呼び出せるのは、同じモジュールの中にあるからです。main から grades::is_at_least(95, 90) を呼ぶと、“function is_at_least is private”(関数 is_at_least は非公開)というエラーになります。これがねらいです。モジュールは小さな公開APIだけを見せて、ヘルパー関数は自分の中にしまっておけます。
mod name { ... }で、関連するコードを名前付きのモジュールにまとめる。- モジュールの中身はデフォルトで非公開。モジュールの外に見せたいものには
pubを付ける。 useでパスをスコープに持ち込めば、毎回フルパスを書かずに済む。- 複数ファイルのプロジェクトでは、
mod name;(中身なし)と書くと、同じ名前のファイルからモジュールのコードが読み込まれる。 - モジュールの中のコードは、そのモジュールの非公開の要素をいつでも使える。ヘルパーは非公開のままにして、呼び出す側に必要なものだけを公開しよう。
理解度チェック
Section titled “理解度チェック”理解度チェック
1. mod の中に書いた要素は、デフォルトでは公開と非公開のどちらですか?
2. モジュールのコードをその場に書かず、math.rs から読み込ませるにはどう書きますか?
スコア: 0 / 2
さらに詳しく
Section titled “さらに詳しく”- 肥大化していくプロジェクトをパッケージ、クレート、モジュールを利用して管理する — The Rust Programming Language 日本語版:パッケージ、クレート、モジュールと、それらがどう組み合わさるのかを解説した公式の章です。
- モジュールを定義して、スコープとプライバシーを制御する — The Rust Programming Language 日本語版:モジュールツリーの具体例と、ルールをまとめた便利な早見表があります。
- モジュールを複数のファイルに分割する — The Rust Programming Language 日本語版:入れ子のモジュール用のフォルダも含めて、複数ファイルの構成をさらにくわしく説明しています。
- モジュール — Rust By Example 日本語版:可視性、
use、super、selfを実際に動かせる例です。 - Visibility and privacy — The Rust Reference(英語):「自分のクレートの中だけで公開」を意味する
pub(crate)も含めた、正確なルールです。