やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのモジュール(mod)

 モジュールとは、関数、列挙、構造体などの定義をグルーピングする単位、だと思う。

成果物

モジュールとは

 モジュールとは、関数、列挙、構造体などの定義をグルーピングする単位、だと思う。

  • mod { ... }でモジュールを宣言する
  • 接頭辞pubの有無によってPublic/Privateを設定できる
  • useでモジュールとその中の定義をスコープに入れることができる

コード

重複回避

src/lib.rs

mod network {
    fn connect() {}
}
mod client {
    fn connect() {}
}

 connectという同名の関数があるが、名前は重複しない。network::connect(), client::connect()になる。

ネスト(サブ・モジュール)

src/lib.rs

mod network {
    fn connect() {}
    mod client {
        fn connect() {}
    }
}

## 外部ファイル化

src/lib.rs

mod client; mod network { fn connect() {} // mod client; // ここでもOK }

src/client.rs

fn connect() {}

 コンパイラは標準で`src/lib.rs`ファイルだけを探す。もし別ファイルを読み込ませたければsrc/lib.rs内で`mod 名前;`とする。`mod client;`とすることで、指定したモジュール(ファイル)を探すようコンパイラに指示することになる。(おそらくC/C++における`#include`みたいなもの)

 src/client.rsには`mod client { ... }`がない。呼出元で`mod client;`としているため。もしここでも`mod client { ... }`で囲ってしまったら`client::client::connect()`になってしまう。

### すべて外部ファイル化


src/lib.rs

mod client; mod network;

src/client.rs

fn connect() {}

src/network.rs

fn connect() {}

### 外部ファイル化(サブモジュールがあるとき)

src/lib.rs

mod client; mod network;

src/client.rs

fn connect() {}

src/network/mod.rs

fn connect() {}

src/network/server.rs

fn connect() {}

 特殊なのは`src/モジュール名/mod.rs`のところ。

 なお、1ファイルにすべて書くと以下。

src/lib.rs

mod client { fn connect() {} } mod network { fn connect() {} mod server { fn connect() {} } }

### 外部ファイル化(異階層で同名のモジュールがあるとき)

* プロジェクト
    * src/
        * lib.rs
        * client/
            * mod.rs
        * network/
            * mod.rs
            * client.rs

src/lib.rs

pub mod client; pub mod network;

[cfg(test)]

mod tests { use super::*; #[test] fn it_works() { assert_eq!(2 + 2, 4); client::connect(); network::connect(); network::client::connect(); } }

src/client/mod.rs

fn connect() {}

src/network/mod.rs

pub mod client; fn connect() {}

 基本的にこれでいいと思う。ディレクトリにしておけば、将来サブモジュールの追加がされてもすぐに対応できる。

 ただ、[モジュールファイルシステムの規則](https://doc.rust-jp.rs/book/second-edition/ch07-01-mod-and-the-filesystem.html#a%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E8%A6%8F%E5%89%87)には反しているように読める。

> fooという名前のモジュールにサブモジュールがなければ、fooの定義は、 foo.rsというファイルに書くべきです。

 サブモジュールがないのに、`foo.rs`というファイルに書かず、`foo/mod.rs`というファイルに書いている。

# モジュールファイルシステムの規則

* fooという名前のモジュールにサブモジュールがなければ、fooの定義は、 foo.rsというファイルに書くべきです。
* fooというモジュールに本当にサブモジュールがあったら、fooの定義は、 foo/mod.rsというファイルに書くべきです。

 つまり、サブモジュールの有無によってファイル構成や記述が変わる。

 「サブモジュールの有無に関わらず、どちらか一方に統一してくれ」と思うが、それだと以下のような問題があって不可能。

## `mod ファイル名;` + 同階層ファイル

src/lib.rs

mod client { fn connect() {} } mod network { fn connect() {} mod client { fn connect() {} } }

 同名のモジュールが2つある。

* ルートのモジュール`client`
* `network`モジュールのサブモジュール`client`

 これを同階層`src/`配下のファイル名にすると重複してしまう。もしこの方法しか許されないなら、階層違いであっても一度使われた名前はもう使えないことになってしまう。

### 疑問1

 私としては、以下のように解釈してくれてもいいのでは、と思ってしまったが。以下のようなことはrustではできない。

* `src/client.rs`
* `src/network.client.rs`

 よく考えてみるとこれは将来的に困る。仮にできたとしたら、ファイルシステム的に管理しづらくなるだろう。1ディレクトリにファイルが無数にできることになってしまうから。それよりディレクトリで細分化されたほうが俯瞰しやすい。

### 疑問2

 nullのときは「NULLかそれ以外の2つの状態があって、それがバグを生みやすい」みたいな話をしていたのだが、このモジュール生成においても同様ではないか。

 つまり「サブモジュールがあるかないかの2つの状態があって、それが混乱を生みやすい」。パッとみてわかりにくいし書きにくい。`main.rs`, `lib.rs`, `mod.rs`が特殊な予約ファイル名であることがわかりにくさの原因か。

 でも、そのルールを覚えてしまえば、それほど苦ではないのか? サブモジュールがあるときに同一階層で名前重複が起こりうる。それを回避する方法がこれだけだから。サブモジュールが増えたときも外部ファイル化できる。`mod.rs`に`pub mod サブモジュール名;`を追記して、`サブモジュール名.rs`ファイルを追加すればいい。`src/lib.rs`と`src/サブモジュール.rs`の関係とおなじ。そう思えば難しくないかも?

## `mod ファイル名;` + ディレクトリ + mod.rs + サブモジュール名.rs

* プロジェクト
    * lib.rs
    * client/
        * mod.rs
    * network/
        * mod.rs
        * client.rs

 これでいいのでは?

* https://doc.rust-jp.rs/book/second-edition/ch07-01-mod-and-the-filesystem.html#a%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E5%88%A5%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E7%A7%BB%E3%81%99

> network::clientモジュールをsrc/client.rsファイルに抽出することはできません。 もうトップ階層のclientモジュールとして存在するからです! clientとnetwork::client双方のコードをsrc/client.rsファイルに書くことができたら、 コンパイラは、コードがclient用なのか、network::client用なのか知る術を失ってしまいます。

 上記がよくわからなかった。つまり、以下の場合はダメってこと? 「ファイルに抽出」というのがわからん。コードで示してくれ。以下、憶測。

* プロジェクト
    * lib.rs
        * `pub mod client;`
    * client.rs
        * `pub mod network`
        * `use network::client`
    * network/
        * mod.rs
        * client.rs

src/client.rs

pub mod network; // error[E0583]: file not found for module network use network::client; pub fn connect() {}

 動かしてみたら上記コメントアウトしたエラーが出た。ドキュメントがいっているのはこういうことで合っているのか? 今まさにそれについて学習中だから判断できない……。頼むからちゃんと全部コードで示してくれ。自然言語とプログラミング言語の両方で示しておくれよ。

# まとめ

* モジュールとファイル構成
    * 同一ファイル: `lib.rs`内で`mod モジュール名 {}`
    * 別ファイル: `lib.rs`, `モジュール名.rs`
    * 別ファイル: `lib.rs`, `モジュール名/mod.rs`

# 所感

 覚えることは少ないほうがいい。よって私は勝手に「src/モジュール名/mod.rs」の方法で統一する。大は小を兼ねるってことで合ってるよね?

# 対象環境

* <time datetime="2019-06-16T15:57:20+0900" title="実施日">2019-06-16</time>
* [Raspbierry pi](https://ja.wikipedia.org/wiki/Raspberry_Pi) 3 Model B+
* [Raspbian stretch](https://ja.wikipedia.org/wiki/Raspbian) 9.0 2018-11-13
* [bash](https://ja.wikipedia.org/wiki/Bash) 4.4.12(1)-release

$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

# 前回まで

* [Rustを学んでみたい(プログラミング言語)](http://ytyaru.hatenablog.com/entry/2020/07/15/000000)
* [Rustの環境構築](http://ytyaru.hatenablog.com/entry/2020/07/16/000000)
* [RustでHelloWorld](http://ytyaru.hatenablog.com/entry/2020/07/17/000000)
* [Rustの和訳ドキュメント](http://ytyaru.hatenablog.com/entry/2020/07/18/000000)
* [Cargoでプロジェクト作成・ビルド・実行](http://ytyaru.hatenablog.com/entry/2020/07/19/000000)
* [クレートとは?](http://ytyaru.hatenablog.com/entry/2020/07/20/000000)
* [Rustで関数を使ってみる](http://ytyaru.hatenablog.com/entry/2020/07/21/000000)
* [Rustでモジュールを使ってみる](http://ytyaru.hatenablog.com/entry/2020/07/22/000000)
* [Rustで乱数を生成する(rand)](http://ytyaru.hatenablog.com/entry/2020/07/23/000000)
* [Rustで標準入力する(std::io::stdin().read_line())](http://ytyaru.hatenablog.com/entry/2020/07/24/000000)
* [RustでMatch判定する(match)](http://ytyaru.hatenablog.com/entry/2020/07/25/000000)
* [Rustでprintとread_lineを1行にする方法](http://ytyaru.hatenablog.com/entry/2020/07/26/000000)
* [Rustで数当てゲーム](http://ytyaru.hatenablog.com/entry/2020/07/27/000000)
* [クレート名にドット.が使えない](http://ytyaru.hatenablog.com/entry/2020/07/28/000000)
* [Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable `x`](http://ytyaru.hatenablog.com/entry/2020/07/29/000000)
* [Rustのimmutable束縛とconst定数の違い](http://ytyaru.hatenablog.com/entry/2020/07/30/000000)
* [RustのREPL、evcxrのインストールに失敗した](http://ytyaru.hatenablog.com/entry/2020/07/31/000000)
* [Rustでコンパイルするときの変数未使用warningを消す](http://ytyaru.hatenablog.com/entry/2020/08/01/000000)
* [Rustの変数(再代入、再宣言(シャドーイング))](http://ytyaru.hatenablog.com/entry/2020/08/02/000000)
* [Rustのシャドーイングについて](http://ytyaru.hatenablog.com/entry/2020/08/03/000000)
* [イミュータブルについて(副作用)](http://ytyaru.hatenablog.com/entry/2020/08/04/000000)
* [Rustの定数(const)](http://ytyaru.hatenablog.com/entry/2020/08/05/000000)
* [Rustのデータ型(数値)](http://ytyaru.hatenablog.com/entry/2020/08/06/000000)
* [Rustのデータ型(論理)](http://ytyaru.hatenablog.com/entry/2020/08/07/000000)
* [Rustのデータ型(文字)](http://ytyaru.hatenablog.com/entry/2020/08/08/000000)
* [Rustのデータ型(タプル)](http://ytyaru.hatenablog.com/entry/2020/08/09/000000)
* [Rustのデータ型(配列)](http://ytyaru.hatenablog.com/entry/2020/08/10/000000)
* [Rustの関数](http://ytyaru.hatenablog.com/entry/2020/08/11/000000)
* [Rustのif式](http://ytyaru.hatenablog.com/entry/2020/08/12/000000)
* [Rustのくりかえし文(loop)](http://ytyaru.hatenablog.com/entry/2020/08/13/000000)
* [Rustのくりかえし文(while)](http://ytyaru.hatenablog.com/entry/2020/08/14/000000)
* [Rustのくりかえし文(for)](http://ytyaru.hatenablog.com/entry/2020/08/15/000000)
* [Rustの所有権(ムーブ)](http://ytyaru.hatenablog.com/entry/2020/08/16/000000)
* [Rustの所有権(関数)](http://ytyaru.hatenablog.com/entry/2020/08/17/000000)
* [Rustの所有権(スライス)](http://ytyaru.hatenablog.com/entry/2020/08/18/000000)
* [Rustの構造体(定義とインスタンス化)](http://ytyaru.hatenablog.com/entry/2020/08/19/000000)
* [Rustの構造体(プログラム例)](http://ytyaru.hatenablog.com/entry/2020/08/20/000000)
* [Rustの構造体(メソッド)](http://ytyaru.hatenablog.com/entry/2020/08/21/000000)
* [Rustの列挙型(enum)](http://ytyaru.hatenablog.com/entry/2020/08/22/000000)
* [Rustの列挙型(enum)](http://ytyaru.hatenablog.com/entry/2020/08/22/000000)
* [Rustの列挙型(enum)](http://ytyaru.hatenablog.com/entry/2020/08/22/000000)
* [Rustのmatch(制御フロー演算子)](http://ytyaru.hatenablog.com/entry/2020/08/23/000000)
* [RustでNULLを扱う(Option, Some, None)](http://ytyaru.hatenablog.com/entry/2020/08/24/000000)
* [NULL参照は10億ドルの失敗だった](http://ytyaru.hatenablog.com/entry/2020/08/25/000000)
* [Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)](http://ytyaru.hatenablog.com/entry/2020/08/26/000000)
* [RustのIfLet(matchの糖衣構文)](http://ytyaru.hatenablog.com/entry/2020/08/27/000000)
* [Rustのプロジェクト構造](http://ytyaru.hatenablog.com/entry/2020/08/28/000000)
* [Rustのcargoでライブラリ&テスト(単体、結合)](http://ytyaru.hatenablog.com/entry/2020/08/29/000000)