ドキュメンテーションテスト

The primary way of documenting a Rust project is through annotating the source code. Documentation comments are written in CommonMark Markdown specification and support code blocks in them. Rust takes care about correctness, so these code blocks are compiled and used as documentation tests.

/// 最初の行には関数の機能の短い要約を書きます。
///
/// 以降で詳細なドキュメンテーションを記述します。コードブロックは三重のバッククォートで始まり、
/// 暗黙的に`fn main()`と`extern crate <クレート名>`で囲われます。
/// `doccomments`クレートをテストしたいときには、次のように記述します。
///
/// ```
/// let result = doccomments::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

/// 一般的に、ドキュメンテーションコメントは
/// "Examples", "Panics", "Failures" という章から成ります。
///
/// 次の関数は除算を実行します。
///
/// # Examples
///
/// ```
/// let result = doccomments::div(10, 2);
/// assert_eq!(result, 5);
/// ```
///
/// # Panics
///
/// 第2引数がゼロであればパニックします。
///
/// ```rust,should_panic
/// // ゼロで除算するとパニックします
/// doccomments::div(10, 0);
/// ```
pub fn div(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Divide-by-zero error");
    }

    a / b
}

ドキュメンテーションコメント中のコードブロックは、cargo testコマンドで自動的にテストされます。

$ cargo test
running 0 tests

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

   Doc-tests doccomments

running 3 tests
test src/lib.rs - add (line 7) ... ok
test src/lib.rs - div (line 21) ... ok
test src/lib.rs - div (line 31) ... ok

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

ドキュメンテーションテストの目的

ドキュメンテーションテストの主な目的は、実行例を示すことであり、これは最も大切なガイドラインの一つにもなっています。これにより、ドキュメントの例を実際に動くコードとして使うことができます。しかしながら、main()を返すために、?を使うとコンパイルに失敗してしまいます。ドキュメンテーションでコードブロックの一部を隠す機能で、この問題に対処できます。つまり、fn try_main() -> Result<(), ErrorType>を定義しておきながらそれを隠し、暗黙のmainの内部でunwrapするのです。複雑なので、例を見てみましょう。

/// ドキュメンテーションテストで、`try_main`を隠して使います。
///
/// ```
/// # // 行頭に `#` を置くと行が隠されるが、コンパイルには成功します。
/// # fn try_main() -> Result<(), String> { // ドキュメントの本体を囲う行
/// let res = doccomments::try_div(10, 2)?;
/// # Ok(()) // try_mainから値を返します
/// # }
/// # fn main() { // unwrap()を実行します。
/// #    try_main().unwrap(); // try_mainを呼びunwrapすると、エラーの場合にパニックします。
/// # }
/// ```
pub fn try_div(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Divide-by-zero"))
    } else {
        Ok(a / b)
    }
}

参照

  • RFC505 ドキュメンテーションのスタイルについて
  • API Guidelines ドキュメンテーションのガイドラインについて