異なるモジュールの名前を参照する
モジュール名を呼び出しの一部に使用して、モジュール内に定義された関数の呼び出し方法を解説しました。
リスト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(); }
リスト7-7: 囲まれたモジュールをフルパス指定して関数を呼び出す
見てお分かりの通り、フルパス指定した名前を参照すると非常に長ったらしくなります。 幸い、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を提供してください。
次は、自分の素晴らしく綺麗なコードで使用できる標準ライブラリのコレクションデータ構造について見ていきましょう。