異なるモジュールの名前を参照する
モジュール名を呼び出しの一部に使用して、モジュール内に定義された関数の呼び出し方法を講義しました。
リスト7-7に示したnested_modules
関数の呼び出しのような感じですね。
ファイル名: src/main.rs
pub mod a { pub mod series { pub mod of { pub fn nested_modules() {} } } } fn main() { a::series::of::nested_modules(); }
見てお分かりの通り、フルパス指定した名前を参照すると非常に長ったらしくなります。 幸い、Rustには、これらの呼び出しをもっと簡潔にするキーワードが用意されています。
use
キーワードで名前をスコープに導入する
Rustのuse
キーワードは、呼び出したい関数のモジュールをスコープに導入することで、
長ったらしい関数呼び出しを短縮します。以下は、
a::series::of
モジュールをバイナリクレートのルートスコープに持ってくる例です:
Filename: src/main.rs
pub mod a { pub mod series { pub mod of { pub fn nested_modules() {} } } } use a::series::of; fn main() { of::nested_modules(); }
use a::series::of;
の行は、of
モジュールを参照したい箇所全部でフルパスのa::series::of
を使用するのではなく、
of
を利用できることを意味しています。
このuse
キーワードは、指定したものだけをスコープに入れます: モジュールの子供はスコープに導入しないのです。
そのため、nested_modules
関数を呼び出したい際に、それでもまだof::nested_modules
を使わなければならないのです。
以下のように、代わりにuse
で関数を指定して、関数をスコープに入れることもできました:
pub mod a { pub mod series { pub mod of { pub fn nested_modules() {} } } } use a::series::of::nested_modules; fn main() { nested_modules(); }
そうすれば、モジュールをすべて取り除き、関数を直接参照することができます。
enumもモジュールのようにある種の名前空間をなすので、enumの列挙子をuse
でスコープに導入することもできます。
どんなuse
文に関しても、一つの名前空間から複数の要素をスコープに導入する場合、波かっことお尻にカンマを使用することで列挙できます。
こんな感じで:
enum TrafficLight { Red, Yellow, Green, } use TrafficLight::{Red, Yellow}; fn main() { let red = Red; let yellow = Yellow; let green = TrafficLight::Green; }
Green
をuse
文に含んでいないので、まだGreen
バリアント用にTrafficLight
名前空間を指定しています。
Globで全ての名前をスコープに導入する
ある名前空間の要素を全て一度にスコープに導入するには、*
表記が使用でき、これはglob(塊)演算子と呼ばれます。
この例は、あるenumの列挙子を各々を列挙せずに全てスコープに導入しています:
enum TrafficLight { Red, Yellow, Green, } use TrafficLight::*; fn main() { let red = Red; let yellow = Yellow; let green = Green; }
*
演算子はTrafficLight
名前空間に存在する全て公開要素をスコープに導入します。
あまりglobは使用するべきではありません: 便利ではありますが、globは予想以上の要素を引き込んで、
名前衝突を引き起こす可能性があるのです。
super
を使用して親モジュールにアクセスする
この章の頭で見かけたように、ライブラリクレートを作成する際、Cargoはtests
モジュールを用意してくれました。
今からそれについて詳しく掘り下げていくことにしましょう。communicator
プロジェクトでsrc/lib.rsを開いてください:
ファイル名: src/lib.rs
pub mod client;
pub mod network;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
第11章でテストについて詳しく説明しますが、これでこの例の一部が持つ意味がわかったのではないでしょうか:
他のモジュールに隣接するtests
という名前のモジュールがあり、このモジュールはit_works
という名前の関数を含んでいます。
特別な注釈があるものの、tests
モジュールもただのモジュールです!よって、モジュール階層は以下のような見た目になります:
communicator
├── client
├── network
| └── client
└── tests
テストは、ライブラリ内でコードの準備運動を行うためのものなので、このit_works
関数からclient::connect
関数を呼び出してみましょう。
まあ、尤も今のところ、機能の検査は何もしないんですけどね。これはまだ動きません:
ファイル名: src/lib.rs
# #![allow(unused_variables)] #fn main() { #[cfg(test)] mod tests { #[test] fn it_works() { client::connect(); } } #}
cargo test
コマンドを呼び出してテストを実行してください:
$ cargo test
Compiling communicator v0.1.0 (file:///projects/communicator)
error[E0433]: failed to resolve. Use of undeclared type or module `client`
(エラー: 解決に失敗しました。未定義の型、またはモジュール`client`を使用しています)
--> src/lib.rs:9:9
|
9 | client::connect();
| ^^^^^^ Use of undeclared type or module `client`
コンパイルが失敗しましたが、なぜでしょうか?src/main.rsのように、関数の直前にcommunicator::
を配置する必要はありません。
なぜなら、間違いなくここでは、communicator
ライブラリクレート内にいるからです。
原因は、パスが常に現在のモジュールに対して相対的になり、ここではtests
になっているからです。
唯一の例外は、use
文内であり、パスは標準でクレートのルートに相対的になります。
tests
モジュールは、client
モジュールがスコープに存在する必要があるのです!
では、どうやってモジュール階層を一つ上がり、tests
モジュールのclient::connect
関数を呼び出すのでしょうか?
tests
モジュールにおいて、先頭にコロンを使用して、コンパイラにルートから始めて、フルパスを列挙したいと知らせることもできます。
こんな感じで:
::client::connect();
あるいは、super
を使用して現在のモジュールからモジュール階層を一つ上がることもできます。
以下のように:
super::client::connect();
この例では、これら二つの選択はそれほど異なるようには見えませんが、モジュール階層がもっと深ければ、
常にルートから書き始めるのは、コードを長ったらしくする原因になります。そのような場合、
super
を使用して現在のモジュールから兄弟のモジュールに辿り着くのは、いいショートカットになります。
さらに、コードのいろんなところでルートからパスを指定してから、サブ木構造を別の箇所に移してモジュール構造を変化させた場合、
複数箇所でパスを更新する必要に陥り、面倒なことになるでしょう。
各テストでsuper::
と入力しなければならないのも不快なことですが、それを解決してくれる道具をもう見かけています:
use
です!super::
の機能は、use
に与えるパスを変更するので、ルートモジュールではなく、
親モジュールに対して相対的になります。
このような理由から、ことにtests
モジュールにおいてuse super::somthing
は通常、
最善策になるわけです。故に、今ではテストはこんな見た目になりました:
ファイル名: src/lib.rs
# #![allow(unused_variables)] #fn main() { #[cfg(test)] mod tests { use super::client; #[test] fn it_works() { client::connect(); } } #}
再度cargo test
を実行すると、テストは通り、テスト結果出力の最初の部分は以下のようになるでしょう:
$ cargo test
Compiling communicator v0.1.0 (file:///projects/communicator)
Running target/debug/communicator-92007ddb5330fa5a
running 1 test
test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
まとめ
これでコードを体系化する新しいテクニックを知りましたね!これらのテクニックを使用して、 関連のある機能をまとめ上げ、ファイルが長くなりすぎるのを防ぎ、ライブラリの使用者に整理整頓された公開APIを提供してください。
次は、自分の素晴らしく綺麗なコードで使用できる標準ライブラリのコレクションデータ構造について見ていきましょう。