Rustのmod文のメモ。
modは、モジュールを宣言する文。
モジュールは、構造体・列挙型・トレイトや関数を(ディレクトリーのように)階層化して管理するもの。
(Javaのパッケージに相当)
モジュールの定義方法(modの使い方)には以下の2種類がある。
mod モジュール名 { 〜サブモジュールや構造体や関数等の定義〜 }
mod モジュール名;
モジュール内の構造体や関数等を使用する際は、モジュールのパスを指定する必要がある。
モジュール名1::モジュール名2::〜::構造体名
なお、パスをいちいち書いていると長くなってしまうので、use文でインポートしておくことが出来る。
波括弧でブロックを作り、その中に構造体や関数等を直接定義することが出来る。
mod モジュール名 { 〜サブモジュールや構造体や関数等の定義〜 }
mod sub { pub struct MyStruct { value: i32, } impl MyStruct { pub fn new(value: i32) -> MyStruct { MyStruct { value } } pub fn print_value(&self) { println!("MyStruct.value = {}", self.value); } } }
fn main() { let s = sub::MyStruct::new(123); s.print_value(); }
同じソースファイル内であっても、モジュール内の構造体にアクセスしたい場合は「sub::
」のようにモジュール名を指定する必要がある。
また、モジュール内の構造体やメソッドが見られる状態(可視性が「pub
」や「pub(crate)
」)でないとアクセスできない。
Rustの場合、srcディレクトリーの中にrsファイルを置いただけでは、コンパイル対象にならない。
(当然ながら、コンパイル対象になっていないソースファイルの中で定義された構造体や関数等を使用することは出来ない)
mod文でソースファイルをモジュールとして宣言する必要がある。(ファイル名がモジュール名になる)
よそにあるソースファイルをモジュールとして宣言することで、そのソースファイルがコンパイル対象になる。
mod モジュール名;
(Javaのpackage文はいわばフルパスでパッケージ名を定義するが、Rustのmod宣言は相対パス的に子モジュールの名前のみを宣言する)
モジュールとして宣言できるソースファイルはどの場所にあるものでもいいというわけではなく、直接の子供に当たるソースファイルだけが宣言できる。
このmod文を書く場所は、基本的にmain.rs(もしくはlib.rs)と各ディレクトリー直下のmod.rs。(この場合は必ずmod.rsというファイル名でなければならない)
複数のサブディレクトリーを掘っていく場合、各ディレクトリーのmod.rsにmod文を書いていくことになる。
複数のサブモジュールがある場合、mod文はそのサブモジュールの数だけ記述する。
ソースファイル | 宣言する場所 | mod文の例 |
---|---|---|
src/sub0.rs | src/main.rs |
mod sub0; |
src/sub1/mod.rs | src/main.rs |
mod sub1; |
src/sub1/sub11.rs | src/main.rs |
mod sub1; |
src/sub1/mod.rs |
pub mod sub11; |
|
src/sub1/sub11.rs src/sub1/sub12.rs |
src/main.rs |
mod sub1; |
src/sub1/mod.rs |
pub mod sub11; pub mod sub12; |
|
src/sub1/sub2/sub120.rs | src/main.rs |
mod sub1; |
src/sub1/mod.rs |
pub mod sub2; |
|
src/sub1/sub2/mod.rs |
pub mod sub120; |
例えばsrc/sub1/sub12.rsを使うつもりでsrc/sub1/sub11.rsに「mod sub12;
」と書いた場合、(mod文が書かれた)sub11がモジュールとして解釈され、src/sub1/sub11/sub12.rsか、src/sub1/sub11/sub12/mod.rsを探そうとする。そしてそのファイルが見つからなくてコンパイルエラーになる。
そういった場所にファイルを作ればコンパイルは通るが、紛らわしいと思う…。
src/main.rsで「mod sub;
」と宣言したとき、「src/sub.rs」と「src/sub/mod.rs」の両方が存在していると、コンパイルエラーになる。
どちらかにする必要がある。
Rustの歴史的には、mod.rsを使う方が古いらしい?
(Javaではpackage-info.javaを各パッケージに置くので、mod.rsを使う方がそれと似ていて納得いくw)
src/main.rsと同じディレクトリーにあるsub0.rsをモジュールとして宣言する例。
pub struct MyStruct { value: i32, } impl MyStruct { pub fn new(value: i32) -> MyStruct { MyStruct { value } } pub fn print_value(&self) { println!("MyStruct.value = {}", self.value); } }
mod sub0; fn main() { let s = sub0::MyStruct::new(123); s.print_value(); }
src/sub1/というディレクトリーを作ってその中にmod.rsというファイル名のソースファイルを作成し、sub1をモジュールとして宣言すると、mod.rsの中で定義した構造体や関数等はsub1のモジュールとして扱える。
この場合、ファイル名はmod.rsでなければならない。
pub struct MyStruct { value: i32, } impl MyStruct { pub fn new(value: i32) -> MyStruct { MyStruct { value } } pub fn print_value(&self) { println!("MyStruct.value = {}", self.value); } }
mod sub1; fn main() { let s = sub1::MyStruct::new(123); s.print_value(); }
pub struct MyStruct { value: i32, } impl MyStruct { pub fn new(value: i32) -> MyStruct { MyStruct { value } } pub fn print_value(&self) { println!("MyStruct.value = {}", self.value); } }
pub mod sub120;
pub mod sub2;
mod sub1; fn main() { let s = sub1::sub2::sub120::MyStruct::new(123); s.print_value(); }
モジュール内の構造体や関数等を使用する際は、モジュールのパスを指定する必要がある。
モジュール名1::モジュール名2::〜::構造体名
各モジュールは見える状態(可視性が「pub
」や「pub(crate)
」)でないとコンパイルエラーになる。
モジュールツリーのパスは、デフォルトでは自分のモジュールからの相対パスになる。
形式 | 説明 | 例 | |
---|---|---|---|
絶対パス(crate) | src直下からモジュール名を全て書く。 | crate::sub1::sub2::MyStruct |
|
相対パス(self) | 自分のモジュールからの相対パス。 | 自分がsrc/sub1.rsのとき | self::sub2::MyStruct |
相対パス(self省略) | 自分がsrc/sub1.rsのとき | sub2::MyStruct |
|
親からのパス(super) | 親モジュールからの相対パス。 | 自分がsrc/sub1.rsのとき | super::sub1::sub2::MyStruct |
なお、パスをいちいち書いていると長くなってしまうので、use文でインポートしておくことが出来る。