Rustのmoduleシステムが複雑だと感じる人が多いらしいので、頭の中でどうイメージしたら良いかを追求しました。
* 筆者はまだコンパイラが読めていないので、中で何が起こっているかは一旦無視し、どう考えるべきかにこだわりました。
mod
もuse
もextern crate
もmod 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 use
はpub mod {...}
、use foo::bar as wow
はmod wow {...}
と考えてOK
use
は絶対パス、実際に使うときは相対パス
以下のコードはコンパイルしません。
extern crate reqwest;
pub mod foo {
pub fn hello() {
let client = reqwest::Client::new(); // compile error
}
}
extern crate reqwest
はpub 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}を指すこととします。
- 親moduleからは直接な子itemにだけ
pub
関係なしにアクセスできるが、それよりも内側はpub
でないとアクセスできない。 - 子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
関数にアクセスできています。これは、middle
がinner1
にアクセスでき(ルール1)、inner1
はmiddle
とmiddle
がアクセスできる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 crate
とuse
は全てrootファイルのトップに」というノウハウが浸透しているので、以上に示したような複雑な例が現れることはあまりないと思いますが・・
以上でした