Rust
41
どのような問題がありますか?

この記事は最終更新日から3年以上が経過しています。

投稿日

更新日

Rustのmoduleがすっきりわかる

Rustのmoduleシステムが複雑だと感じる人が多いらしいので、頭の中でどうイメージしたら良いかを追求しました。
* 筆者はまだコンパイラが読めていないので、中で何が起こっているかは一旦無視し、どう考えるべきかにこだわりました。

moduseextern cratemod foo {}で置き換えてOK

まずは大前提から。それぞれ役割は違いますが、外からみたら大体同じなので、コードを書いてるときは頭の中でmod {}と置き換えるととても楽になります。

mod

以下はfoo.rsまたはfoo/mod.rsを探してインポートしてくれます。

mod foo;

なので、コード上ではこれと一緒ですね。

mod foo {
    // foo.rsまたはfoo/mod.rsの中身
}

extern crate

extern crate foo;

extern crateも、結局は

pub mod foo {
    // crateの中身(lib.rs)
}

と考えてほぼ差し支えません。

use

useも、基本的に同じですが、mod以外をuseすることもできるので、そのときはそれぞれで差し替えてください。

use foo::bar::baz;

bazがmoduleの場合:

mod baz {
    // foo::bar::bazの中身
}

また、実体は別にあるのでsymlinkのようなものとイメージすると良いと思います。

その他

pub usepub mod {...}use foo::bar as wowmod wow {...}と考えてOK

useは絶対パス、実際に使うときは相対パス

以下のコードはコンパイルしません。

extern crate reqwest;

pub mod foo {
    pub fn hello() {
        let client = reqwest::Client::new(); // compile error
    }
}

extern crate reqwestpub mod reqwest{...}と同値でしたね。そう考えると、fooからreqwestは直接参照できません。正しくは::reqwest::Client::new();、またはsuper::reqwest::Client::new();になります。
では、次はどうでしょうか。

extern crate reqwest;

pub mod foo {
    use reqwest::Client;
    pub fn hello() {
        let client = Client::new();
    }
    pub mod bar {
        use reqwest::Client;
        pub fn hello() {
             let client = Client::new();
        }
    }  
}

普段見ないようなコードかと思いますが、ここではfoo内でもbar内でもuse reqwest::Clientになってますね。
もう少し良い例を挙げたかったですが、まとめます。

  • useを使うときは常にrootから(絶対パス)。相対パスで指定したい場合は、self::fooのようにする。
  • その他の参照では相対パスで探す。絶対パスで指定したい場合は::foo::barのようにする。

親子のプライバシー

これがちょっと複雑ですが、なるべく少ない文字数でまとめたいと思います。
* ここでは、itemは{mod, fn, impl, struct}を指すこととします。

  1. 親moduleからは直接な子itemにだけpub関係なしにアクセスできるが、それよりも内側はpubでないとアクセスできない。
  2. 子moduleは親module全てにpub関係なしにアクセスでき、それぞれの親がアクセスできるitemにもアクセスできる。

どういうことでしょうか。以下に例を示します。

mod outer {
    mod middle {
        mod inner1 {
            pub fn func() {}
        }
        mod inner2 {
            use outer::middle::inner1::func; // ok
        }
    }
    use outer::middle::inner1::func; // compile error
}

この例ではpub modは一つもありませんが、inner2からinner1内のfunc関数にアクセスできています。これは、middleinner1にアクセスでき(ルール1)、inner1middlemiddleがアクセスできるitem全てにアクセスできるため(ルール2)、結果的に兄弟にもアクセスできることになるためです。moduleにアクセスできるということは、それに含まれるpubなitem全てにアクセスできるということなので、funcを呼び出すことができます(pubが付いていなければ無理です)。
それに対し、outerからはmiddleにアクセスできますが、middleの中のpubなitemしか見ることができないため、inner1の中のfuncには届きません。

例外など

pub(crate), pub(restricted), pub(in ..)などについても書こうと思いましたが、シンプルにするため割愛します。

まとめ

  • use, mod, extern crateまとめて考えよう
  • useは絶対パス
  • 親は直接の子しか見えないが、子は祖先全員見ることができる

少しでもイメージしやすくなればと思います。
正直「extern crateuseは全てrootファイルのトップに」というノウハウが浸透しているので、以上に示したような複雑な例が現れることはあまりないと思いますが・・
以上でした

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
harvath
http://stackoverflow.com/users/3891638/cozyconemotel
この記事は以下の記事からリンクされています

コメント

この記事にコメントはありません。
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
データに関する記事を書こう!
~
新人プログラマ応援 - みんなで新人を育てよう!
~
41
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー