S-JIS[2024-09-16] 変更履歴

Rust modメモ

Rustのmod文のメモ。


概要

modは、モジュールを宣言する文。

モジュールは、構造体列挙型トレイト関数を(ディレクトリーのように)階層化して管理するもの。
(Javaのパッケージに相当)


モジュールの定義方法(modの使い方)には以下の2種類がある。


モジュール内の構造体や関数等を使用する際は、モジュールのパスを指定する必要がある。

モジュール名1::モジュール名2::〜::構造体名

モジュールツリーのパスの指定方法

なお、パスをいちいち書いていると長くなってしまうので、use文でインポートしておくことが出来る。


モジュール(ブロック)の定義方法

波括弧でブロックを作り、その中に構造体や関数等を直接定義することが出来る。

mod モジュール名 {
    〜サブモジュールや構造体や関数等の定義〜
}

モジュールのブロックの例

src/main.rs:

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直下にあるソースファイルの例

src/main.rsと同じディレクトリーにあるsub0.rsをモジュールとして宣言する例。

src/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);
    }
}

src/main.rs:

mod sub0;

fn main() {
    let s = sub0::MyStruct::new(123);
    s.print_value();
}

ディレクトリーの下にあるmod.rsの例

src/sub1/というディレクトリーを作ってその中にmod.rsというファイル名のソースファイルを作成し、sub1をモジュールとして宣言すると、mod.rsの中で定義した構造体や関数等はsub1のモジュールとして扱える。
この場合、ファイル名はmod.rsでなければならない

src/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);
    }
}

src/main.rs:

mod sub1;

fn main() {
    let s = sub1::MyStruct::new(123);
    s.print_value();
}

サブディレクトリーにあるrsファイルの例

src/sub1/sub2/sub120.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);
    }
}

src/sub1/sub2/mod.rs

pub mod sub120;

src/sub1/mod.rs

pub mod sub2;

src/main.rs:

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文でインポートしておくことが出来る。


Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま