Rust勉強中 - その14 -> ライブラリとドキュメント

# 自己紹介
出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

# 環境
新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.38.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

# 前回
前回は式とエラーハンドリングについて学びました。
[Rust勉強中 - その13](https://qiita.com/deta-mamoru/items/2b0c5fca44d592e0d453)

# Module
moduleにはアイテムを含めます。
アイテムとは、関数や型、型エイリアス、implブロック、定数、Module、インポート、externブロックのことです。
内部ファイルにmoduleを書く際は以下のようにします。

```Rust
mod module_name {
    // items
    ...
}
```
例えば、以下のように書きます。

```Rust:main.rs
mod inside_sum {
    pub fn pub_sum(a: i32, b: i32) -> i32 {
        a+b
    }

    fn plv_sum(a: i32, b:i32) -> i32 {
        a+b
    }
}

fn main() {
    // ./src/main.rs
    println!("inside_sum::pub_sum(1, 1)        = {}",   inside_sum::pub_sum(1, 1));        // inside_sum::pub_sum(1, 1) = 2
    // println!("inside_sum::plv_sum(1, 1)       = {}", inside_sum::plv_sum(1, 1));        // Error: function is private
}
```
このとき、modに囲まれた関数の先頭にpubを付ければ外からアクセス可能なパブリック関数となります。もし、付けなければ外からアクセスできないプライベート関数になります。上記の場合は、pub_sum関数はパブリック関数で、plv_sum関数はプライベート関数です。

## ファイルの分割
ここにきてついに、ファイルを分割する方法を学びます。
moduleごとにファイルを分割したい場合があります。
先ほどは直接main.rsのファイル中にmoduleを記述していましたが、外部ファイルにモジュールを移動させます。

```Rust:outside_sum.rs
pub fn pub_sum(a: i32, b: i32) -> i32 {
    a+b
}

fn plv_sum(a: i32, b:i32) -> i32 {
    a+b
}
```
```Rust:main.rs
mod outside_sum;
 
fn main() {
    // ./src/main.rs
    println!("inside_sum::pub_sum(1, 1)        = {}",   inside_sum::pub_sum(1, 1));        // inside_sum::pub_sum(1, 1) = 2
    // println!("inside_sum::plv_sum(1, 1)       = {}", inside_sum::plv_sum(1, 1));        // Error: function is private
    // ./src/outside_sum.rs
    println!("outside_sum::pub_sum(1, 1)       = {}",   outside_sum::pub_sum(1, 1));       // outside_sum::pub_sum(1, 1) = 2
    // println!("outside_sum::plv_sum(1, 1)      = {}", outside_sum::plv_sum(1, 1));       // Error: function is private
    println!("::outside_sum::pub_sum(1, 1)       = {}", ::outside_sum::pub_sum(1, 1));     // ::outside_sum::pub_sum(1, 1) = 2
}
```
moduleファイルoutside_sum.rsにはアイテムを淡々と列挙し、main.rsではそのmoduleをインポートするために`mod outside_sum;`を記述しています。main関数内の最後の行で`::outside_sum::pub_sum(1, 1)`としており、先頭に::演算子を付けています。これは絶対パスを意味しており、ルートディレクトリからみてのoutside_sum::pub_sumという意味です。
複数のインポートは`{}`で囲って列挙します。
サブモジュールから親モジュールのアイテムのアクセスするには、明示的にサブモジュール内で親モジュールをインポートしてあげる必要があります。

```Rust
use super::path;
```
この時、superは現在のmoduleの上のmoduleを表します。
また、selfを使うと現在のmoduleを表します。

```Rust
use self::path;
```
このようにして、相対パスを使うことができます。

*を使えば、パブリックアイテムを全てインポートできます。

```Rust
use path::*;
```

## preludeとは
std::preludeはよく使う型や関数を自動的にインポートします。他のpreludeは*を使って、インポートしてほしいという目印として考えておきます。

# ライブラリ
複数の別のcrateで、moduleを使いたくなった場合、moduleをライブラリとして作成しほかのcrateでも使えるようにする必要があります。
もし、既存の実行ファイルcrateをライブラリにする場合、

1. main.rsをlib.rsに変更
2. main関数をsrc/bin/bin_name.rsに移動
3. lib.rsにアイテムを列挙

コンパイラはsrc/lib.rsがあると、そのプロジェクトをライブラリとしてビルドします。

新規でライブラリを作成する場合は、

```
$ cargo new --lib lib_name
```
こうすることで[lib_name]のライブラリを作成できます。

## ライブラリ内の実行ファイル
src/binディレクトリに実行ファイル.rsを置くと、実行ファイルがそのライブラリをインポートし実行することができます。

```Rust:library/src/lib.rs
pub fn sum(a: i32, b: i32) -> i32 {
    a+b
}
fn sub(a: i32, b: i32) -> i32 {
    a-b
}
```
```Rust:library/src/bin/sum.rs
extern crate library;
use library::sum;

fn main() {
    println!("sum(1, 1) = {}", sum(1, 1));
}
```
```Rust:library/src/bin/sub.rs
extern crate library;
use library::sub;

fn main() {
    println!("sub(1, 1) = {}", sub(1, 1)); // Error: sub is private
}
```
ライブラリcrateでsrc/binディレクトリの実行ファイルを実行するには、`cargo run --bin bin_filename`を実行します。

```
$  cargo run --bin sum                                                                     Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target\debug\sum.exe`
sum(1, 1) = 2
```
```
$ cargo run --bin sub                                                                    Compiling library v0.1.0 (C:\Users\deta\hack\rust\library)
error[E0603]: function `sub` is private
 --> src\bin\sub.rs:2:14
  |
2 | use library::sub;
  |              ^^^

error: aborting due to previous error
```
sub関数はプライベート関数なのでエラーを出してくれます。

別のディレクトリにあるcrateをインポートするにはCargo.tomlを以下のように追記します。

```toml:Cargo.toml
...
[dependencies]
crate_name = {path = "../crate/path"}
```

# テスト
## ユニットテスト
ユニットテストは、`#[test]`属性を指定することで次のアイテムがテスト関数になります。ユニットテストは実行ファイルでもライブラリでも機能します。

```Rust
pub fn sum(a: i32, b: i32) -> i32 {
    a+b
}
#[test]
fn test1_sum() {
    assert_eq!(sum(1, 1), 2);
}
#[test]
fn test2_sum() {
    assert_eq!(sum(sum(1, 1), sum(1, 1)), 4);
}
```
テストを実行するには`cargo test`を実行します。

```
$ cargo test                                                                               Compiling tests v0.1.0 (C:\Users\deta\hack\rust\tests)
    Finished dev [unoptimized + debuginfo] target(s) in 1.31s
     Running target\debug\deps\tests-142668c48ca884f1.exe

running 2 tests
test test1_sum ... ok
test test2_sum ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```
また、`cargo test test1`とするとtest1が含まれるテストのみ実行することができます。

```
$ cargo test test1                                                                          Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running target\debug\deps\tests-142668c48ca884f1.exe

running 1 test
test test1_sum ... ok

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

もし、テストコードが大きくなってきて、インポートコードやサポート関数などが必要になってきた場合、testsモジュールとして定義します。

```Rust
#[cfg(test)]
mod tests {
    fn prepare(...) {
        ...
    }

    #[test]
    fn test_f1(...) {
        ...
    }

    #[test]
    fn test_f2(...) {
        ...
    }
}
```

## 結合テスト
結合テストはトップディレクトリ(srcと同じディレクトリ)にtestsを作成して、そのディレクトリに.rsファイルとして書いていきます。

```Rust:src/lib.rs
pub fn sum(a: i32, b: i32) -> i32 {
    a+b
}
#[test]
fn test1_sum() {
    assert_eq!(sum(1, 1), 2);
}
#[test]
fn test2_sum() {
    assert_eq!(sum(sum(1, 1), sum(1, 1)), 4);
}

pub fn sub(a: i32, b: i32) -> i32 {
    a-b
}
```
```Rust:tests/test.rs
extern crate tests;
use tests::{sum, sub};

#[test]
fn test_sum_sub() {
    let a = 1;
    let b = 1;
    assert_eq!(sum(sum(a, b), sub(a, b)), 2);
}
```
この状態で、cargo testを実行すると、単体テストと結合テスト両方実行されます。

```
$ cargo test                                                                               Compiling tests v0.1.0 (C:\Users\deta\hack\rust\tests)
    Finished dev [unoptimized + debuginfo] target(s) in 1.13s
     Running target\debug\deps\tests-142668c48ca884f1.exe

running 2 tests
test test2_sum ... ok
test test1_sum ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target\debug\deps\sum-96a857838ee1e966.exe

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target\debug\deps\test-3a76ac4fc4aa1ddd.exe

running 1 test
test test_sum_sub ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```
もし、結合テストのみ行いたい場合は、`cargo test --test test_name`を実行します。

```
$ cargo test --test test                                                                    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running target\debug\deps\test-3a76ac4fc4aa1ddd.exe

running 1 test
test test_sum_sub ... ok

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

# ドキュメント
`cargo doc`コマンドで、HTMLドキュメントを作成できます。

```
$ cargo doc --no-deps --open
```
--no-depsオプションは依存するcrateのドキュメントは作成しません。--openオプションは作成したドキュメントをブラウザで開きます。
生成されたドキュメントはtarget/docに格納されます。
ドキュメントは、pubが付いたアイテムとドキュメントコメント(ドクコメント、doc comments)を見つけて生成されます。

## ドキュメントコメント
ドキュメントコメントは///または、#[doc]属性、//!で書きます。

* ///はその行がドキュメントコメントになります。
* #[doc]属性に指定された文字列がドキュメントコメントになります。
* //!はcrateかmoduleに対してドキュメントコメントになります。

ドキュメントコメントはMarkdown形式に対応しています。

### コードブロック
ドキュメントコメント内にコードブロックを記述することができます。さらに記述したコードブロックのコードをテストすることができます。
コードブロックは、ドキュメントコメント内でバッククォート一つまたは三つか、スペース4つで認識されます。

```Rust:lib.rs
//! sum and sub library!

/// sum library!
///
/// return 2
///
///     assert_eq!(tests::sum(1, 1), 2);
///
/// return 3
///
///     assert_eq!(tests::sum(1, 2), 3);
///
pub fn sum(a: i32, b: i32) -> i32 {
    a+b
}
#[test]
fn test1_sum() {
    assert_eq!(sum(1, 1), 2);
}
#[test]
fn test2_sum() {
    assert_eq!(sum(sum(1, 1), sum(1, 1)), 4);
}

/// sub library!
///
/// return 0
///
///     assert_eq!(tests::sub(1, 1), 0);
///
/// return 1
///
///     assert_eq!(tests::sub(2, 1), 1);
///
pub fn sub(a: i32, b: i32) -> i32 {
    a-b
}
```
これらのコードブロックをテストするには例のごとく`cargo test`を実行します。または、ドキュメントテストだけ行いたい場合は、`cargo test --doc`でもできます。

```
$ cargo test --doc                                                                          Finished dev [unoptimized + debuginfo] target(s) in 0.02s
   Doc-tests tests

running 4 tests
test src\lib.rs - sum (line 12) ... ok
test src\lib.rs - sub (line 29) ... ok
test src\lib.rs - sub (line 34) ... ok
test src\lib.rs - sum (line 7) ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
```

# ソース
```Rust:module/src/outside_sub_calc/mod.rs
pub mod sub;
pub mod mul;
pub mod div;
```
```Rust:module/src/outside_sub_calc/div.rs
pub fn div(a: f64, b:f64) -> Option<f64> {
    if b==0.0 {
        None
    } else {
        Some(a/b)
    }
}
```
```Rust:module/src/outside_sub_calc/mul.rs
pub fn mul(a: i32, b: i32) -> i32 {
    a*b
}
```
```Rust:module/src/outside_sub_calc/sub.rs
pub fn pub_sum(a: i32, b: i32) -> i32 {
    a+b
}

fn plv_sum(a: i32, b:i32) -> i32 {
    a+b
}
```
```Rust:module/src/outside_sum.rs
pub fn pub_sum(a: i32, b: i32) -> i32 {
    a+b
}

fn plv_sum(a: i32, b:i32) -> i32 {
    a+b
}
```
```Rust:module/src/main.rs
mod inside_sum {
    pub fn pub_sum(a: i32, b: i32) -> i32 {
        a+b
    }

    fn plv_sum(a: i32, b:i32) -> i32 {
        a+b
    }
}

mod outside_sum; 
mod outside_sub_calc;
use outside_sub_calc::mul;
use outside_sub_calc::div::div;

fn main() {
    // ./src/main.rs
    println!("inside_sum::pub_sum(1, 1)        = {}",   inside_sum::pub_sum(1, 1));        // inside_sum::pub_sum(1, 1) = 2
    // println!("inside_sum::plv_sum(1, 1)       = {}", inside_sum::plv_sum(1, 1));        // Error: function is private
    // ./src/outside_sum.rs
    println!("outside_sum::pub_sum(1, 1)       = {}",   outside_sum::pub_sum(1, 1));       // outside_sum::pub_sum(1, 1) = 2
    // println!("outside_sum::plv_sum(1, 1)      = {}", outside_sum::plv_sum(1, 1));       // Error: function is private
    println!("::outside_sum::pub_sum(1, 1)       = {}", ::outside_sum::pub_sum(1, 1));     // ::outside_sum::pub_sum(1, 1) = 2
    println!("outside_sub_calc::sub::sub(1, 1) = {}",   outside_sub_calc::sub::sub(1, 1)); // outside_sub_calc::sub::sub(1,1) = 0
    println!("outside_sub_calc::mul(2, 2)      = {}",   mul::mul(2, 2));                   // mul::mul(2, 2) = 4
    println!("div(2.0, 2.0)                    = {:?}", div(2.0, 2.0));                    // div(2.0, 2.0) = 1.0
}
```
```Rust:tests/src/lib.rs
//! sum and sub library!

/// sum library!
///
/// return 2
///
///     assert_eq!(tests::sum(1, 1), 2);
///
/// return 3
///
///     assert_eq!(tests::sum(1, 2), 3);
///
pub fn sum(a: i32, b: i32) -> i32 {
    a+b
}
#[test]
fn test1_sum() {
    assert_eq!(sum(1, 1), 2);
}
#[test]
fn test2_sum() {
    assert_eq!(sum(sum(1, 1), sum(1, 1)), 4);
}

/// sub library!
///
/// return 0
///
///     assert_eq!(tests::sub(1, 1), 0);
///
/// return 1
///
///     assert_eq!(tests::sub(2, 1), 1);
///
pub fn sub(a: i32, b: i32) -> i32 {
    a-b
}
```

今回はここまでー。
ついに、ファイルを分割できるようになりました。