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ファイルのトップに」というノウハウが浸透しているので、以上に示したような複雑な例が現れることはあまりないと思いますが・・
以上でした
コメント