異なるモジュールの名前を参照する

モジュール名を呼び出しの一部に使用して、モジュール内に定義された関数の呼び出し方法を解説しました。 リスト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;
}

Greenuse文に含んでいないので、まだ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を提供してください。

次は、自分の素晴らしく綺麗なコードで使用できる標準ライブラリのコレクションデータ構造について見ていきましょう。