Rustのモジュールシステム
Rustではパブリック関数には pub をつけます。
pub fn hello() {
println!("hello!");
}
pub をつけることで他のファイルでインポートすることが可能となります。
mod greet;
fn main() {
greet::hello();
}
これは以下のように1つで書いたものと同じです。
fn main() {
greet::hello();
}
mod greet {
pub fn hello() {
println!("Hello");
}
}
これはgreetがサブモジュールであることを意味しています。 また、以下のように use を使うと省略してインポートすることが可能です。
use greet::hello;
mod greet;
fn main() {
hello();
}
サブモジュール
さて先ほどmainからgreetをインポートした際に、greetがサブモジュールとみなされていると説明しました。
さて、以下のように3つのファイルになったらどうなるでしょう。
pub fn hello() {
println!("hello!");
}
mod greet;
pub fn say() {
greet::hello()
}
mod user;
fn main() {
user::say();
}
実行するとfile not found for module greet
とコンパイルエラーとなります。
これは、userがmainのサブモジュール、greetがuserのサブモジュールとみなされているためです。わかりやすく1つのファイルで書いてみます。
fn main() {
user::say();
}
mod user {
pub fn say() {
greet::hello()
}
mod greet {
pub fn hello() {
println!("Hello");
}
}
}
ではどう修正すれば良いでしょうか?やり方は簡単に2つあります。
- 先ほどの1つのファイルのようにgreet.rsをuserのサブモジュールにしてあげる
- greetもmainのサブモジュールにしてあげる
サブモジュールを入れ子にする
fn main() {
user::say();
}
mod user {
pub fn say() {
greet::hello()
}
mod greet {
pub fn hello() {
println!("Hello");
}
}
}
これのようにサブモジュールが入れ子になるようにしてディレクトリ構想を変更します。
.
├── main.rs
├── user
│ └── greet.rs // userのサブモジュール
└── user.rs
greet.rs
の場所が変わるだけで、中身は変わりません。
pub fn hello() {
println!("hello!");
}
また、これは以下のようにも書けます。
.
├── main.rs
├── user
│ └── greet
│ └── mod.rs
└── user.rs
フロントのディレクトリベースルーティング(e.g. page router)とファイルベースルーティング(e.g. App router)の違いに少し似ています。Next.jsでは後者の方が推奨されていますが、Rustではディレクトリベースの方が2018 editionで導入されて推奨されています。ファイルベースは2015 editionとなります。
サブモジュールを作成せずにimportする
crate::
もしくはsuper
使用することでサブモジュールからサブモジュールを呼び出すことができます。superは直近の親からの相対パスで、crateを使用した場合はプロジェクトのルートからの絶対パスとなります。
fn main() {
user::say();
}
mod user {
pub fn say() {
crate::greet::hello()
}
}
mod greet {
pub fn hello() {
println!("Hello");
}
}
これをファイルで分けて書くと、以下のようになります。
mod greet;
mod user;
fn main() {
user::say();
}
pub fn say() {
crate::greet::hello()
}
pub fn hello() {
println!("Hello");
}
lib.rs
先ほどのサブモジュールを作成せずにimportするで、さらにモジュールが増えたらどうなるでしょうか。
mod foo;
mod foo2;
mod foo3;
// ・・・
fn main() {
foo::hoge();
}
上記のようにmain.rs
でのインポートが増えてしまいます。
そこでRustではlib.rs
なるものを作成して、そこにインポートをまとめることが一般的となっております。
fn main() {
app::user::say();
}
pub fn hello() {
println!("Hello");
}
pub fn say() {
crate::greet::hello()
}
mod greet;
pub mod user;
また、先ほどのmain.rs
は以下のように書けます。
use app::user::say;
fn main() {
say();
}
また、useをmain.rs
で使いたくない場合はlib.rs
にuseを書くこともできます。
pub use crate::user::say;
mod greet;
mod user;
lib.rs
にuseを書いた場合は、main.rs
は以下のように変更します。
fn main() {
app::say();
}