Rust のモジュールシステム

  • 55
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

クレート

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

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

cargo new foo --bin

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

cargo new bar

とする。

モジュール

ひとつのファイルに書く

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

新規作成
$ cargo new mod_example1 --bin
ファイル構成
$ tree
|   Cargo.toml
|
\---src
        main.rs
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();
}
実行
$ cd mod_example1
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!

ファイルをわける

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

新規作成
$ cargo new mod_example2 --bin
ファイル構成
$ tree
|   Cargo.toml
|
\---src
        foo.rs
        main.rs

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

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

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

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

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
実行
$ cd mod_example2
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!

ディレクトリをわける

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

新規作成
$ cargo new mod_example3 --bin
ファイル構成
$ tree
|   Cargo.toml
|
\---src
    |   main.rs
    |
    \---foo
            bar.rs
            mod.rs
src/foo/mod.rs
pub fn hello() {
  println!("Hello, World!!");
}

pub mod bar; // さらに内部のモジュール bar を公開
src/foo/bar.rs
pub fn hello() {
  println!("Hello, World!!!");
}

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

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

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

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
実行
$ 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

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

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

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

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

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

クレートをわける

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

新規作成
$ cargo new mod_example4 --bin
ファイル構成
$ tree
|   Cargo.toml
|
\---src
    |   lib.rs
    |   main.rs
    |
    \---foo
        |   mod.rs
        |
        \---bar
                mod.rs
src/foo/bar/mod.rs
fn hello() {
  println!("Hello, World!!!");
}
src/foo/mod.rs
fn hello() {
  println!("Hello, World!!");
}

pub mod bar;
src/lib.rs
pub mod foo;
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() などと書かないといけない。

実行
$ cd mod_example4
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!

ポイント

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

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

src/foo/mod.rs

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

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

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

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

GitHub をつかう

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

モジュール側

新規作成
$ cargo new happy
ファイル構成
$ tree
|   Cargo.toml
|
\---src
    |   lib.rs
    |
    \---foo
        |   mod.rs
        |
        \---bar
                mod.rs

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

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

利用側

新規作成
$ cargo new mod_example5 --bin
ファイル構成
$ tree
|    Cargo.toml
|
\---src
        main.rs
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 がつかわれる。

src/main.rs
extern crate happy;

use happy::foo;

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

fn main() {
  hello();
  foo::hello();
  foo::bar::hello();
}
実行
$ cd mod_example5
$ cargo run
Hello, World!
Hello, World!!
Hello, World!!!

git submodule にする必要もない。