🦀

Rustのmodule完全に理解した。

2021/10/12に公開

あれれ~? おっかしいぞ~? おじちゃんこの記事読んで「モジュール完全に理解した」って言ってなかったっけー?

ホンマすいません。また忘れました。

外してはいけないところ

ルートファイル

Rustにはルートファイルという考え方があります。以下のどちらかがrootになります。

  • src/main.rs
  • src/lib.rs

Cargo.tomlで明示的に設定して変更することも可能ですが、ほとんどの場合上記のデフォルトが使用されることでしょう。

ルートファイルからのmod宣言が必要

ここが一番重要です。Rustでは自分が定義したモジュールはルートファイルからmodキーワードを使って宣言が必要です。

さもないとそのファイルは無いも同然として扱われ、コンパイルすらされません。

./src
├── commands.rs
└── main.rs
main.rs
mod commands;  //これが無いとcommands.rsは無視される

main() {...}
commands.rs
pub fn commandA() {...}

ファイル名がモジュール名になる

上記の例ではcommands.rsというファイルはcommandsというモジュールの中身を定義していることになります。

main.rsで宣言したmod commandsの実体がcommands.rsだと言うことになります。もし、command.rsが存在しないと、main.rsのエラーとして以下が発生します。

error[E0583]: file not found for module `commands`

useではだめ

さて、もう一つmoduleを扱うためのキーワードとしてuseがあります。

main.rs
use commands;

main() {...}

これでも良さそうな気がしてしまいますよね? ダメなんです。

error[E0432]: unresolved import `commands`

useは宣言済みの名前を取り込むだけです。
例えばチュートリアルに出てくるuse std::io;ですが、これは組み込みのモジュールとして定義済みのものをioという省略形の名前で使えるようにしているだけの話です。

ディレクトリ階層

ここまではcommands.rsmain.rsと同階層にフラットに並んでいました。

├── commands.rs
├── main.rs
├── types
│   └── dog.rs

ここで、types/dog.rsをモジュールとして参照したい場合はどうなるでしょうか?

::を使った階層的mod宣言はできない

main.rs
mod types::dog;

error: expected one of `;` or `{`, found `::`

::は使えないと怒られます。

ディレクトリと同名のファイルが必要

├── commands.rs
├── main.rs
├── types
│   └── dog.rs
└── types.rs    //types ディレクトリと同名のファイルが必要

このファイルでdogモジュールを宣言します。(対応する実体は./types/dog.rs)

types.rs
pub mod dog;

するとmain.rsから以下のようにdogモジュールを使用可能になります。

main.rs
mod types;

fn main() {
  types::dog::wang();
}

つまりはこういうことになります。

  • main.rsに宣言されたmod typesの実体はtypes.rs
  • types.rsに宣言されたmod dogの実体はtypes/dog.rs

モジュールのスコープ

はて、しれっとpubが付いていますね。

types.rs
pub mod dog;

このpubを消すとどうなるでしょうか?

error[E0603]: module `dog` is private
  --> src/main.rs:16:12
   |
16 |     types::dog::wang();
   |            ^^^ private module
   |
note: the module `dog` is defined here
  --> src/types.rs:1:1
   |
1  | mod dog;
   | ^^^^^^^^```

types::modはprivateなのでmain.rsからは参照できないと言われます。そう、pubをつけないとtypesモジュール内のみで参照可能なprivateなモジュールになります。

忘れた方が良いこと

mod.rs

時折、mod.rsというファイル名のファイルを置きなさいという情報が見つかります()が、edition2015以前の過去の情報です。edition2018以降では過去の互換のためだけに残っている方法なので忘れましょう。

mod hoge {...}

公式ドキュメントでもそうなのですが、mod hoge {...}で階層的なモジュール宣言の方法が述べられています。

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

これはあまり使わない、無理矢理に1ファイルに複数モジュールを宣言するための書き方なので一旦忘れましょう。

mod {...}と1ラインのmod hogeの宣言は同じmodキーワードですがニュアンスが違います。

参考資料

Discussion