やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのmod参照方法(`mod 子モジュール名;`, `use 要素名;`, `extern crate クレート名;`, `super`)

 この辺、細かい説明がなくてよくわからんかった。ので、使ってみた。

成果物

参照

 上記を学習しているとき、当然のようにmod ... ;, use ... ;, extern crate ... ;の構文が出てきた。でも、こいつらの動作がよくわからない。そのままmodpubの説明されても納得しがたい。そこでそれらについて動作確認してルールを確認してみた。

modの参照方法

 別ファイルに定義されたモジュールを参照する方法は3つある。

  • extern crate クレート名;
  • mod 子モジュール名;
  • use モジュール名::要素名;

 参照元と被参照の親子関係などによって、どれを使うべきかが決まる。3つの内、下にあるものほど使える場所が多い。

extern crate クレート名;

 main.rsエントリポイントでlib.rsをロードするときに使える。

cargo new my_crate --lib
cargo test
  • my_crate/
    • src/
      • main.rs
      • lib.rs
      • my_mod_1/
        • mod.rs

my_crate/src/main.rs

extern crate my_crate; // 同ディレクトリの`lib.rs`を読込

fn main() {
    assert_eq!(extern_crate_0::my_mod_1::public(), String::from("my_mod_1::public()"));
//    assert_eq!(extern_crate_0::my_mod_1::private(), String::from("my_mod_1::private()")); // error[E0603]: function `private` is private
}

 extern cratelib.rsでは使えなかった。

my_crate/src/lib.rs

//extern crate extern_crate_0; // error[E0463]: can't find crate for `extern_crate_0`
pub mod my_mod_1;

#[cfg(test)]
mod tests {
//    extern crate my_crate; // error[E0463]: can't find crate for `my_crate`
//    extern crate super::my_crate; // error: expected identifier, found keyword `super`
//    use super::my_mod_1; // error[E0432]: unresolved import `super::my_mod_1`
//    use my_mod_1; // error[E0432]: unresolved import `my_mod_1`
//    mod my_mod_1; // error[E0583]: file not found for module `my_mod_1`
//    mod super::my_mod_1; // error: expected identifier, found keyword `super`
    use super::*;

    #[test]
    fn test_my_mod_1_public() {
        assert_eq!(my_mod_1::public(), String::from("my_mod_1::public()")); // error[E0433]: failed to resolve: use of undeclared type or module `my_crate`
    }
    #[test]
    fn test_my_mod_1_private() {
//        assert_eq!(my_mod_1::private(), String::from("my_mod_1::private()")); // error[E0603]: function `private` is private
    }
}

 extern cratemod.rsでは使えなかった。

my_crate/src/my_mod_1/mod.rs

//extern crate my_crate; // error[E0463]: can't find crate for `my_crate`

pub fn public() -> String { String::from("my_mod_1::public()") }
fn private() -> String { String::from("my_mod_1::private()") }

mod 子モジュール名;

 外部ファイル化された子モジュールを宣言するための構文。

 外部ファイル化されたモジュールの場合、pubであるか否かの情報がない。よって、それを直上の親モジュールが指定してやる。そのための構文。

宣言

  • pub mod 子モジュール名;とすると、これを宣言したモジュールを介して参照可(先祖・子孫に関係なく)
  • mod 子モジュール名;とすると、これを宣言したモジュールを介して参照不可。ただし子孫モジュール配下からはsuper::を介して参照可

制約

  • main.rs, lib.rs, mod.rs等で使える(主にlib.rs, mod.rsで使うはず)
  • 外部ファイル化されたモジュールのみ対象(ファイル内ならmod my_mod_1 { ... }のように実装される。それの代わり)
  • 直下の子のみ
    • mod super::my_mod_1;のように先祖は参照不可(error: expected identifier, found keyword super
    • mod my_mod_1::my_mod_2;のように直下の子より深い子孫は参照不可(error: expected one of ; or {, found ::

別ファイルにある子モジュールを参照

 lib.rsから直下モジュールmy_mod_1/mod.rsを参照する。

src/lib.rs

mod my_mod_1;

fn a() {
    my_mod_1::public();
//    my_mod_1::private(); // error[E0603]: function `private` is private
}

src/my_mod_1/mod.rs

pub fn public() -> String { String::from("my_mod_1::public()") }
fn private() -> String { String::from("my_mod_1::private()") }

同一ファイル内にある子モジュールを参照

src/my_mod_1/mod.rs

fn a() {
    sub::my_fn();
}
mod sub {
    fn my_fn() [
        println!("hello");
    }
}

super

 親を参照する。private要素も参照できる。親モジュール側でmod 子モジュール;とすると親子関係が定義され、子モジュール側でsuperが使えるようになる。

 なお、super::super::のように複数回参照すれば先祖も辿れる。

同一ファイル内にある子モジュールtestsから親を参照

 親の参照にはsuperを使う。

src/my_mod_1/mod.rs

pub fn public() -> String { String::from("my_mod_1::public()") }
pub fn private() -> String { String::from("my_mod_1::private()") }

mod tests {
    fn test() [
        super::public();
    }
}
  • 親をuseする必要はない
  • mod 子モジュール;は子モジュールのみ対象なので、子が親を参照するときには使えない
  • extern crate 自クレート名;はmain.rsでしか使えない
別ファイルにある子モジュールから親を参照

src/lib.rs

mod my_mod_1;

fn parent() {
    println!("hello");
}

src/my_mod_1/mod.rs

pub fn pub_call_parent() { super::parent(); }

use

 useは、別ファイルで定義されたpubな要素を参照するときに使う。特に、extern crate クレート名, mod 子モジュール名;では参照できない場合に。

 つまり、main.rsファイル以外で、直下の子モジュールでない要素を参照したいとき、useを使う。たとえば親、兄弟、先祖、子孫、兄弟の子孫や先祖。

  • 親がもつ要素: use super::要素名;
  • 子がもつ要素: use 子モジュール名::要素名;
  • 先祖がもつ要素: use super::super::要素名;
  • 子孫がもつ要素: use 子モジュール名::孫モジュール名::要素名;
  • 兄弟がもつ要素: use super::兄弟モジュール名::要素名;
  • 兄弟の先祖がもつ要素: use ::兄弟の先祖::兄弟の先祖:: ... ::要素名;
  • 兄弟の子孫がもつ要素: use ::兄弟の先祖:: ... ::兄弟の子孫::要素名;

use 要素名;

 useする要素はpubである必要がある。なお、要素はmod, fn, enum, struct, traitと思われる。

 要素のパスは、これを宣言したモジュールを基準とする。つまり子要素を対象とする。

use ::要素名;

 先頭に::があるとルートからのパスとなる。

use モジュール名::要素名;

 これを宣言したモジュールを基準としたパス。つまり子モジュールの要素を対象とする。

use super::要素名

 親の要素もuseできる。なお、先祖の場合はsuper::super::のように辿る数だけsuperを付与すればいい。

use モジュール名::*

 *とすれば、指定したモジュール内で定義されている要素すべてを、そのモジュールの接頭辞なしで参照できるようになる。名前重複しやすくなるため注意。

 他にもsuper::*など指定方法はさまざま。

まとめ

参照方法 呼出元 呼出対象 概要
extern crate クレート名; main.rs lib.rsCargo.tomlで指定した外部クレート
mod モジュール名; main.rs, lib.rs, mod.rs 外部ファイル化した子モジュール
use 要素名; main.rs, lib.rs, mod.rs 外部ファイル化したpubな要素mod,fn,struct,enum,trait(先頭::絶対パス、それ以外で相対パス。末尾::*で全要素を接頭辞なし読込)

 外部ファイルの要素を参照するときのまとめ。誰が、誰を参照するとき、どう宣言し、どう参照するかの一覧。

誰が 誰を どう宣言 どう参照
main.rs(ファイル最上) Cargo.tomlで指定した外部クレート extern crate 外部クレート名; 外部クレート名::要素名;
main.rs(ファイル最上) 同一クレート内の lib.rs extern crate 自クレート名; 自クレート名::要素名;
モジュール(ファイル最上) 直下の子モジュール mod 子モジュール; 子モジュール::要素名;
モジュール, fn 指定パスにある指定した要素 use ::要素;, use 親::子;, use 要素::*, use super::要素; ::要素名::子要素名;, 親::子, 要素名, super::要素, super::super::要素

主な用途

 mod 子モジュール名, use 要素名;, extern crate クレート名;は外部ファイルの要素を参照するときに使う。(C/C++でいう#include的)

extern crate クレート名

 main.rsでのみ使う。

  • 内部lib.rs読込: main.rsでextern crate 自クレート名;とすると自クレート内のlib.rsを読み込む
  • 外部クレート読込: main.rsでextern crate 外部クレート名;とすると外部クレートを読み込む(外部クレートはCargo.tomlで読込を指示する)

 クレート名::要素名で参照できるようになる。

mod 子モジュール名;

 主にlib.rs, mod.rsで使う。直下の外部ファイル化した子モジュールを探して読み込む。pubか否か指定する。

 子モジュール名::要素名で参照できるようになる。

use 要素名;

 main.rs, lib.rs, mod.rs, いずれでも使用可。

 外部ファイル化したモジュールやその他の要素を読み込む。

 参照する要素は以下のようにパス指定する。

  • use ::要素名でルートsrc/配下からの参照パス指定をする
  • use 要素名でそのuse宣言したモジュールを起点とした相対パスとなる

 なお、複数の要素をすべて読込たいときは::*が使える。

  • use 要素名::*要素名配下にあるすべての子要素は要素名::の接頭辞なしに参照できる

 ただし、接頭辞がなくなるため、名前重複しやすくなるので注意。

前回まで