逆引き Rust ライブラリシステム

# はじめに

Rust のライブラリシステムについて逆引きで探せるドキュメントがあるとうれしいと思ったのでまとめてみました。

# 用語

* Rust ではライブラリのパッケージのことをクレートと呼び、クレート内のコードのまとまりをモジュールと呼びます。
* `std::vec::Vec` は `std` クレートの `std::vec` モジュールの `Vec` 構造体です。


# ライブラリ用のプロジェクトを作成する

* `cargo new` コマンドに `--lib` オプションを付けて実行するとライブラリ用のプロジェクトが作成できます

```sh
# greeting プロジェクトを作成する
$ cargo new phrases --lib

# ファイル構成
$ cd phrases
$ find .
./Cargo.toml
./src
./src/lib.rs
```


# モジュールを作成する

* `src/lib.rs` へ `pub mod` でモジュールを定義し、モジュールと同名のファイルでモジュールの実装をしていきます。

```rs:src/lib.rs
// phrases::english モジュールの宣言
pub mod english;
```

```rs:src/english.rs
// phrases::english::hello の定義
pub fn hello() -> String {
    "Hello!".to_string()
}
```


# 同一プロジェクトからモジュールを利用する

* `src/main.rs` を作成して同一プロジェクトから作成したクレートを利用します。
* `src/main.rs` はアプリケーション用の実行ファイルとして扱われ `cargo run` で実行可能になります

```rs:src/main.rs
fn main() {
    assert_eq!(phrases::english::hello(), "Hello!".to_string());
}
```


# サブモジュールを作成する

* 親モジュールの実装ファイル内でサブモジュールの定義を行い、親モジュールと同じ名前のディレクトリを用意し、その下のサブモジュール名のファイルで実装を定義します。

```rs:src/lib.rs
// 親モジュール phrases::english の宣言
pub mod english;
```

```rs:src/english.rs
// 子モジュール phrases::english::greetings の宣言
pub mod greetings;
// 子モジュール phrases::english::farewells の宣言
pub mod farewells;
```

```rs:src/english/greetings.rs
pub fn hello() -> String {
    "Hello!".to_string()
}
```

```rs:src/french/farewells.rs
pub fn goodbye() -> String {
    "Goodbye!".to_string()
}
```

```rs:src/main.rs
fn main() {
    // モジュールを利用する
    assert_eq!(phrases::english::greetings::hello(), "Hello!".to_string());
    assert_eq!(phrases::english::farewells::goodbye(), "Goodbye!".to_string());
}
```


# ライブラリのテストを書く

* ライブラリのユニットテストは個々の実装ファイルなどで `#[test]` 属性を付けた関数として実装します。

```rs:src/english/greetings.rs
pub fn hello() -> String {
    "Hello!".to_string()
}

#[test]
fn test_hello() {
    assert_eq!(hello(), "Hello!".to_string());
}
```

* ライブラリの結合テストは `tests` ディレクトリにテスト関数を実装した .rs ファイルを置いて実装します。
* ライブラリの外部からのテストになるので `extern crate` でクレートを読み込みます

```rs:tests/english.rs
extern crate phrases;

#[test]
fn test_greetings_hello() {
    assert_eq!(phrases::english::greetings::hello(), "Hello!".to_string());
}

#[test]
fn test_farewells_goodbye() {
    assert_eq!(phrases::english::farewells::goodbye(), "Goodbye!".to_string());
}
```


# ライブラリのドキュメントを書く

* クレートの説明は `src/lib.rs` に `//!` を行頭につけたドキュメンテーションコメントとして記載します。 
* ドキュメンテーションコメントは Markdown 記法をサポートします

```rs:src/lib.rs
//! # phrases
//! This is sample code.

pub mod english;
```

* モジュールの説明は `src/english.rs` などモジュールの定義ファイルに `//!` を行頭につけたドキュメンテーションコメントとして記載します。 
* 関数の説明は各関数の定義の上に `///` を行頭につけたドキュメンテーションコメントとして記載します。

```rs:src/english.rs
//! # phrases::english
//! This is sample

///return "Hello!"
pub fn hello() -> String {
    "Hello!".to_string()
}
```

* ドキュメントの確認は `cargo doc` で行います。`--open` はドキュメントをブラウザで開くためのもので `--no-deps` は依存関係にあるクレートのドキュメントは生成しないというものです

```sh
argo doc --open --no-deps
```


# ライブラリを別のプロジェクトから利用する

* ライブラリを github.com などにアップロードし Cargo.toml に依存関係を定義して利用します。
* ブランチ、タグ、リビジョンなどの指定もできます。

```toml:Cargo.toml
[dependencies]
phrases = { git = "https://github.com/nirasan/phrases" }
# phrases = { git = "https://github.com/nirasan/phrases", branch = "master" }
# phrases = { git = "https://github.com/nirasan/phrases", tag = "v1.0.0" }
# phrases = { git = "https://github.com/nirasan/phrases", rev = "10cf8da" }
```

```rs:src/main.rs
extern crate phrases;

fn main() {
    println!("{}", phrases::english::hello());
}
```