Rust のモジュールシステム

# クレート

`Rust` では `crate` (木箱) という単位でプログラムを開発する。`cargo new` で生成される単位が `crate` だ。

`foo` という実行ファイルをつくるには、

~~~bash
cargo new foo --bin
~~~

として `crate` を生成し、`bar` というライブラリをつくるには、

~~~bash
cargo new bar
~~~

とする。


# モジュール

## ひとつのファイルに書く

ひとつのファイルの中にモジュールを作成できる。

~~~bash:新規作成
$ cargo new mod_example1 --bin
~~~

~~~bash:ファイル構成
$ tree
|   Cargo.toml
|
\---src
        main.rs
~~~

~~~rust:src/main.rs
fn hello() {
  println!("Hello, World!");
}

// モジュール
mod foo {
  // 外から使うので pub をつける
  pub fn hello() {
    println!("Hello, World!!");
  }

  // モジュールは入れ子にできる
  // やっぱり外から使うので pub をつける
  pub mod bar {
    pub fn hello() {
      println!("Hello, World!!!");
    }
  }
}

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
~~~

~~~bash:実行
$ cd mod_example1
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!
~~~

## ファイルをわける

いつまでもひとつのファイルに書き続けられない。

~~~bash:新規作成
$ cargo new mod_example2 --bin
~~~

~~~bash:ファイル構成
$ tree
|   Cargo.toml
|
\---src
        foo.rs
        main.rs
~~~

`main.rs` (使う側) とおなじ階層に、モジュール名をファイル名とした `foo.rs` をつくる。

~~~rust:src/foo.rs
pub fn hello() {
  println!("Hello, World!!");
}

pub mod bar {
  pub fn hello() {
    println!("Hello, World!!!");
  }
}
~~~

~~~rust:src/main.rs
fn hello() {
  println!("Hello, World!");
}

mod foo; // モジュールの読み込み

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
~~~

~~~bash:実行
$ cd mod_example2
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!
~~~

## ディレクトリをわける

おなじディレクトリにファイルがたくさんあるのはいやだ。整理したい。

~~~bash:新規作成
$ cargo new mod_example3 --bin
~~~

~~~bash:ファイル構成
$ tree
|   Cargo.toml
|
\---src
    |   main.rs
    |
    \---foo
            bar.rs
            mod.rs
~~~

~~~rust:src/foo/mod.rs
pub fn hello() {
  println!("Hello, World!!");
}

pub mod bar; // さらに内部のモジュール bar を公開
~~~

~~~rust:src/foo/bar.rs
pub fn hello() {
  println!("Hello, World!!!");
}
~~~

`bar.rs` は、`mod bar;` を記述してある `src/foo/mod.rs` とおなじディレクトリに置く。

~~~rust:src/main.rs
fn hello() {
  println!("Hello, World!");
}

mod foo; // foo/mod.rs を参照

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
~~~

~~~bash:実行
$ cd mod_example3
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!
~~~

### ポイント

`mod bar;` と記述すると `./bar.rs` か `./bar/mod.rs` が参照される。両方あるときはエラーになる。

この例だと `src/foo/bar.rs` の代わりに `src/foo/bar/mod.rs` に

~~~rust:src/foo/bar/mod.rs
pub fn hello() {
  println!("Hello, World!!!");
}
~~~

と記述してもいい。もちろんファイルをわけないで、`src/foo/mod.rs` に

~~~rust:src/foo/mod.rs
pub fn hello() {
  println!("Hello, World!!");
}

pub mod bar {
  pub fn hello() {
    println!("Hello, World!!!");
  }
}
~~~

としてもいい。`mod foo;` のところも同じだ。

## クレートをわける

使う側と使われる側は別に扱いたい。

~~~bash:新規作成
$ cargo new mod_example4 --bin
~~~

~~~bash:ファイル構成
$ tree
|   Cargo.toml
|
\---src
    |   lib.rs
    |   main.rs
    |
    \---foo
        |   mod.rs
        |
        \---bar
                mod.rs
~~~

~~~rust:src/foo/bar/mod.rs
fn hello() {
  println!("Hello, World!!!");
}
~~~

~~~rust:src/foo/mod.rs
fn hello() {
  println!("Hello, World!!");
}

pub mod bar;
~~~

~~~rust:src/lib.rs
pub mod foo;
~~~

~~~rust:src/main.rs
extern crate mod_example4; // 外部のクレートを読み込む (この場合は lib.rs)

use mod_example4::foo; // プリフィックスのクレート名を省略できるようにする

fn hello() {
  println!("Hello, World!");
}

fn main() {
  hello();
  foo::hello(); // foo:: でアクセスできる
  foo::bar::hello();
}
~~~

`use` しないと `mod_example4::foo::hello()` などと書かないといけない。

~~~bash:実行
$ cd mod_example4
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!
~~~

#### ポイント
この例だとひとつのクレートにモジュール用と実行形式用が同居している。

`use` は `use some_crate::{some_module1, some_module2};` のように同時に複数指定もできる。

`src/foo/mod.rs` を

~~~rust:src/foo/mod.rs
pub fn hello() {
  println("Hello, World!!");
}

pub use self::bar::hello as greet; // 別名で公開 (mod bar; の先に記述する)

mod bar; // 内部は非公開にしたいので pub を取り除いた
~~~

とすると、`main` で `foo::great()` を呼び出すと、内部的に `foo::bar::hello()` が呼び出されるようになる。実装内部を隠蔽してインタフェースだけを公開するのに便利だ。`self` を指定しているのはこのモジュールからの相対位置をあらわすためで、たんに `bar::hallo` とするとトップレベルからのパスで探索される。また、ひとつ上のモジュールを指定するには `super` を指定する。

## GitHub をつかう

これはモジュール用と実行形式用を完全にわける例。

### モジュール側

~~~bash:新規作成
$ cargo new happy
~~~

~~~bash:ファイル構成
$ tree
|   Cargo.toml
|
\---src
    |   lib.rs
    |
    \---foo
        |   mod.rs
        |
        \---bar
                mod.rs
~~~

ソースコードの内容はクレートをわける例とおなじ。

できたら GitHub に登録しておく。

### 利用側

~~~bash:新規作成
$ cargo new mod_example5 --bin
~~~

~~~bash:ファイル構成
$ tree
|    Cargo.toml
|
\---src
        main.rs
~~~

~~~ini:src/Cargo.toml
[package]
name = "mod_example5"
version = "0.1.0"
authors = ["Shinya Kitaoka <skitaoka@gmail.com>"]

[dependencies.happy]
git = "https://github.com/skitaoka/happy.git"
rev = "cd8c9ad6d035e8cdf99a0ffffe0e0e565d257143"
~~~

リビジョン指定がないと HEAD がつかわれる。

~~~rust:src/main.rs
extern crate happy;

use happy::foo;

fn hello() {
  println!("Hello, World!");
}

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
~~~

~~~bash:実行
$ cd mod_example5
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!
~~~

`git submodule` にする必要もない。