てけもぐ Tech 忘備録

Rust モジュールのファイル分けの話

対象

Rust 初学者

内容

Rust やり始めたんですけど、モジュールのファイル分割で混乱したのでメモ。

モジュールやる前に、今までの想定を引っこ抜く必要がありました。

他の言語だと、ファイルってコンパイルの単位だったり、カプセル化の境界だったりするのですけど、Rust は別。

Rust の「クレート」は、コンパイル単位であるって言いますよね。でもこれ、聞いてもイマイチ分からなかったんですよね。何故かって、ひとつのソースファイルから、当然の様にオブジェクトファイルをそれぞれ作るものだと思ってたから。

Rust では違うらしい...。こちらの説明が図入りで分かりやすかったです。

つまり、それぞれのファイルについてオブジェクトファイルを作るのではなくて、ファイル合体させてからまとめてコンパイル対象にして、より最適化が利くようにすると。

なるほど。その前提の上でモジュールに関するファイル分割を考えると。そういう事らしい。

クレート

クレートはRustにおけるコンパイルの単位です。rustc some_file.rsが呼ばれると、some_file.rs は必ず クレートファイル として扱われます。もしsome_file.rsがmod宣言を含んでいるのならば、コンパイルの 前に 、そのモジュールファイルの中身がmodの位置に挿入されます。言い換えると、それぞれのモジュールが独立にコンパイルされるということはありませんが、それぞれのクレートは互いに独立にコンパイルされるということです。

クレートはバイナリあるいはライブラリ形式でコンパイルされることが可能です。デフォルトではrustcはクレートからバイナリを作り出しますが、この振る舞いは--crate-typeフラグにlibを渡すことでオーバーライドできます。

クレート - Rust by Example 日本語版より(太字にしたのは私)。

なので、mod xxx; と書かれていると、C で言う import の様にファイルが挿入されるんでしょう。なるほど簡単じゃん!

でも混乱する...。実はこれって作り方が原因だと思うんですよね。

2種類の作り方があるという。module_name という名前だとして、

  1. 同じディレクトリに、module_name.rs で作成
  2. module_name/mod.rs として作成

これは、The Rust Programming Language - 日本語版のここ にも書いてありました。1. は 2018 年以降の仕様みたいですね。これ見ると、ん?とは思いますけど、そんなもんかなぁで一度は納得する。でも 1. で実際に作ってみると変。

だって module_name.rs にモジュールの内容が入っている時には、module_name ディレクトリは要らない。でも子供のモジュールを作成するとなったら、その時に module_name 名のディレクトリを作成するんですから。

普通の感覚(普通って何?って話はありますが)だと、モジュール名ディレクトリの下にあるのがそのモジュールのコードでしょう。違う。mod.rs を使わない方法だと、モジュール名のディレクトリの下にあるのは、モジュールの子供のコード。

あと、"::" を使って孫モジュールのインポートは出来ないみたいなので、各親ディレクトリでインポートする必要がある。なるほどなぁ、です。これならファイル構造による縛りが利く。

2.の作り方はシンプルだけど、mod.rs の名前のファイルが沢山出来て、区別しにくいと言えばしにくい。

もうひとつの、use はどうかというと、これはモジュールを使う時に使うパスを省略する略記。以上。mod が分かれば、こちらは混乱しませんでした。

モジュールは、Rust では言語の中での論理的な区切りなんですね。つまりモノ自体はそこにある。

検索したら、結構この手の記事が出てきたので、混乱したのは私だけじゃないハズ。