2017年6月28日水曜日

The Rust Programming Language 2nd 09日目

![](https://doc.rust-lang.org/book/second-edition/]
Apache License Version 2.0

Controlling Visibility with pub

今まで作ってきたライブラリ(ドンガラだが)を外から使ってみる.それには,communicator/src/main.rsを作成して,その中でcommunicatorを呼び出せば良い.

src/main.rs

extern crate communicator;

fn main() {
  communicator::client::connect();
}

ここでcargo buildすると,
shell

error: module `client` is private
 --> src/main.rs:4:3
  |
4 |   communicator::client::connect();
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: Could not compile `communicator`.

To learn more, run the command again with --verbose.

とエラーが出る.clientmoduleがprivateであるというエラーである.

Making a Function Public

pubキーワードによってfunctionやmoduleをpublicにできる.
clientライブラリをpublicにするには,
src/main.rs

pub mod client;
mod network;

とする.ここでcargo buildしても,

error: module `client` is private
 --> src/main.rs:4:3
  |
4 |   communicator::client::connect();
  |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

と怒られる.今度はsrc/client.rsを書き換えてclient::connectをpublicにすると,ビルドできるようになる.

warning: function is never used: `connect`
 --> src/network/mod.rs:1:1
  |
1 |   fn connect() {
  |  _^ starting here...
2 | | }
  | |_^ ...ending here
  |
  = note: #[warn(dead_code)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 0.53 secs

という警告は,network::connectがprivateで,そのライブラリプロジェクトの中でも使われていないから起こる.network::connectとnetwork::server::connectもpublicにする.

Privacy Rules

  1. あるアイテムがpublicであるとき,親moduleを通じてそのアイテムにアクセスできる.
  2. あるアイテムがprivateであるとき,そのアイテムがあるmoduleとその子moduleからしかそのアイテムにはアクセスできない.

Privacy Examples

mod outermost {                             // private                    
  pub fn middle_function() {}               // public in private           
  fn middle_secret_function() {}            // private                      

  mod inside {                              //  private in private           
    pub fn innner_function() {}             //  public in private in private  
    fn secret_function() {}                 // private in private in private 
  }
}

fn try_me() {
  outermost::middle_function();             // PrivateRule1より,accessible
  outermost::middle_secret_function();      // PrivateRule2より,unaccessible
  outermost::inside::inner_function();      // PrivateRule2より,accessible
  outermost::inside::secret_function();     // PrivateRule2より,unaccessible
}

Importing Names

src/main.rs

pub mod a {
  pub mod series{
    pub mod of {
      pub fn nested_modules() {}
    }
  }
}

fn main() {
  a::series::of::nested_modules();
}

のように,module1の中のmodule2,module1の中のfunction1を指定するときには,module1::module2とかmodule1::function1とするのだった.
こうした記法では呼び出しが長くなりすぎる危険があるので,より簡潔に呼ぶ方法が実装されている.

Concise Imports with use

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によって内側にあるmoduleを現時点の名前空間から記述して名前空間に入れることで,そのmoduleの名前だけでそのmoduleを特定できる.

enumもまた名前空間を作るので,useによってenumを簡潔に書ける.同じ名前空間から複数の要素をuseによって今の名前空間に入れるときは{}でまとめられる.

enum TrafficLight {
  Red,
  Yellow,
  Green,
}

use TrafficLight::{Red, Yellow};

fn main() {
  let red = Red;
  let yellow = Yellow;
  let green = TrafficLight::Green
}

### Glob Imports with ```*```
ある名前空間の中の全てを今の名前空間に入れたいとき,```*```を使う.

```rust
enum TrafficLight {
  Red,
  Yellow,
  Green,
}

use TrafficLight::*;

fn main() {
  let red = Red;
  let yellow = Yellow;
  let green = Green;
}




<div class="se-preview-section-delimiter"></div>

*globという.便利だがバグの温床となるので使うときは慎重に考えるべき.

Using super to Access a Parent Module

ライブラリを作成するとき,Cargoは同時にtests moduleを作成する.これについて詳しく述べる.
src/lib.rs

pub mod client;
pub mod network;





<div class="se-preview-section-delimiter"></div>

#[cfg(test)]
mod tests {
  #[test]
  fn it_works() {
  }
}




<div class="se-preview-section-delimiter"></div>

この時点でのcommunicatorの階層構造は

communicator
 ├── client
 ├── network
 |   └── client
 └── tests
 ```
 となっている.```tests::it_works```に```client::connect```を実行するコードを入れてみよう.

 *src/lib.rs*
 ```rust
 #[cfg(test)]
 mod tests {
   #[test]
   fn it_works() {
     client::connect();
   }
 }
 ```
```cargo test```を実行する.


 *shell*
 ```bash
 warning: function is never used: `connect`
 --> src/network/mod.rs:1:1
  |
1 |   pub fn connect() {
  |  _^ starting here...
2 | | }
  | |_^ ...ending here
  |
  = note: #[warn(dead_code)] on by default

  error[E0433]: failed to resolve. Use of undeclared type or module `client`
  --> src/lib.rs:8:5
   |
 8 |     client::connect();
   |     ^^^^^^^^^^^^^^^ Use of undeclared type or module `client`

 error: aborting due to previous error

 error: Could not compile `communicator`.
 Build failed, waiting for other jobs to finish...
 error: build failed




<div class="se-preview-section-delimiter"></div>

rustの中の名前空間は相対的で,testsの中からclient::connectを呼ぶには,一度上の階層に登ってからclient::connectを呼ばなければならない.ただし,usrで呼ぶ場合はルートから名前を指定する.
::を最初につけることで,絶対的に階層を指定できる.例えば::client::connect()とすれば,testsの中からclient::connectを呼べる.あるいは,superキーワードで一つ上の階層に登って,client::connectを呼ぶこともできる.このばあいsuper::client::connectとする.
bashで例えるなら,super.と,::/に対応する.

src/lib.rs

mod network;
pub mod client;

#[cfg(test)]
mod tests {
  #[test]
  fn it_works() {
    client::connect();
  }
}

としてcargo testすると,
shell

Running target/debug/deps/communicator-428d4111ad386458
 
running 1 test
test tests::it_works ... ok
 
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

と,無事テストが完了する.

Running target/debug/deps/communicator-428d4111ad386458

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

と,無事テストが完了する.

0 件のコメント:

コメントを投稿