ドキュメント

ドキュメントはどんなソフトウェアプロジェクトにとっても重要な部分であり、Rustにおいてはファーストクラスです。 プロジェクトのドキュメントを作成するために、Rustが提供するツールについて話しましょう。

rustdoc について

Rustの配布物には rustdoc というドキュメントを生成するツールが含まれています。 rustdoccargo doc によってCargoでも使われます。

ドキュメントは2通りの方法で生成することができます。ソースコードから、そして単体のMarkdownファイルからです。

ソースコードのドキュメントの作成

Rustのプロジェクトでドキュメントを書く1つ目の方法は、ソースコードに注釈を付けることで行います。 ドキュメンテーションコメントはこの目的のために使うことができます。

fn main() { /// Constructs a new `Rc<T>`. /// 新しい`Rc<T>`の生成 /// /// # Examples /// /// ``` /// use std::rc::Rc; /// /// let five = Rc::new(5); /// ``` pub fn new(value: T) -> Rc<T> { // implementation goes here // 実装が続く } }
/// 新しい`Rc<T>`の生成
///
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```
pub fn new(value: T) -> Rc<T> {
    // 実装が続く
}

このコードはこのような見た目のドキュメントを生成します。 実装についてはそこにある普通のコメントのとおり、省略しています。

この注釈について注意すべき1つ目のことは、 // の代わりに /// が使われていることです。 3連スラッシュはドキュメンテーションコメントを示します。

ドキュメンテーションコメントはMarkdownで書きます。

Rustはそれらのコメントを把握し、ドキュメントを生成するときにそれらを使います。 このことは次のように列挙型のようなもののドキュメントを作成するときに重要です。

fn main() { /// The `Option` type. See [the module level documentation](index.html) for more. /// `Option`型。詳細は[モジュールレベルドキュメント](index.html)を参照 enum Option<T> { /// No value /// 値なし None, /// Some value `T` /// `T`型の何らかの値 Some(T), } }
/// `Option`型。詳細は[モジュールレベルドキュメント](index.html)を参照
enum Option<T> {
    /// 値なし
    None,
    /// `T`型の何らかの値
    Some(T),
}

上記の例は動きますが、これは動きません。

fn main() { /// The `Option` type. See [the module level documentation](index.html) for more. /// `Option`型。詳細は[モジュールレベルドキュメント](index.html)を参照 enum Option<T> { /// None, /// No value None, /// 値なし /// Some(T), /// Some value `T` Some(T), /// `T`型の何らかの値 } }
/// `Option`型。詳細は[モジュールレベルドキュメント](index.html)を参照
enum Option<T> {
    None, /// 値なし
    Some(T), /// `T`型の何らかの値
}

次のようにエラーが発生します。

hello.rs:4:1: 4:2 error: expected ident, found `}`
hello.rs:4 }
           ^

この 残念なエラー は正しいのです。ドキュメンテーションコメントはそれらの後のものに適用されるところ、その最後のコメントの後には何もないからです。

ドキュメンテーションコメントの記述

とりあえず、このコメントの各部分を詳細にカバーしましょう。

fn main() { /// Constructs a new `Rc<T>`. /// 新しい`Rc<T>`の生成 fn foo() {} }
/// 新しい`Rc<T>`の生成

ドキュメンテーションコメントの最初の行は、その機能の短いサマリにすべきです。 一文で。 基本だけを。 高レベルから。

fn main() { /// /// Other details about constructing `Rc<T>`s, maybe describing complicated /// semantics, maybe additional options, all kinds of stuff. /// `Rc<T>`の生成についてのその他の詳細。例えば、複雑なセマンティクスの説明、 /// 追加のオプションなどあらゆる種類のもの /// fn foo() {} }
///
/// `Rc<T>`の生成についてのその他の詳細。例えば、複雑なセマンティクスの説明、
/// 追加のオプションなどあらゆる種類のもの
///

この例にはサマリしかありませんが、もしもっと書くべきことがあれば、新しい段落にもっと多くの説明を追加することができます。

特別なセクション

次は特別なセクションです。 それらには # が付いていて、ヘッダであることを示しています。 一般的には、4種類のヘッダが使われます。 今のところそれらは特別な構文ではなく、単なる慣習です。

fn main() { /// # Panics fn foo() {} }
/// # Panics

Rustにおいて、関数の回復不可能な誤用(つまり、プログラミングエラー)は普通、パニックによって表現されます。パニックは、少なくとも現在のスレッド全体の息の根を止めてしまいます。 もし関数にこのような、パニックによって検出されたり強制されたりするような自明でない取決めがあるときには、ドキュメントを作成することは非常に重要です。

fn main() { /// # Failures fn foo() {} }
/// # Failures

もし関数やメソッドが Result<T, E> を戻すのであれば、それが Err(E) を戻したときの状況をドキュメントで説明するのはよいことです。 これは Panics のときに比べると重要性は少し下です。失敗は型システムによってコード化されますが、それでもまだそうすることはよいことだからです。

fn main() { /// # Safety fn foo() {} }
/// # Safety

もし関数が unsafe であれば、呼出元が動作を続けるためにはどの不変条件について責任を持つべきなのかを説明すべきです。

fn main() { /// # Examples /// /// ``` /// use std::rc::Rc; /// /// let five = Rc::new(5); /// ``` fn foo() {} }
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```

4つ目は Examples です。 関数やメソッドの使い方の例を1つ以上含めてください。そうすればユーザから愛されることでしょう。 それらの例はコードブロック注釈内に入れます。コードブロック注釈についてはすぐ後で話しますが、それらは1つ以上のセクションを持つことができます。

fn main() { /// # Examples /// /// Simple `&str` patterns: /// 単純な`&str`パターン /// /// ``` /// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect(); /// assert_eq!(v, vec!["Mary", "had", "a", "little", "lamb"]); /// ``` /// /// More complex patterns with a lambda: /// ラムダを使ったもっと複雑なパターン /// /// ``` /// let v: Vec<&str> = "abc1def2ghi".split(|c: char| c.is_numeric()).collect(); /// assert_eq!(v, vec!["abc", "def", "ghi"]); /// ``` fn foo() {} }
/// # Examples
///
/// 単純な`&str`パターン
///
/// ```
/// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect();
/// assert_eq!(v, vec!["Mary", "had", "a", "little", "lamb"]);
/// ```
///
/// ラムダを使ったもっと複雑なパターン
///
/// ```
/// let v: Vec<&str> = "abc1def2ghi".split(|c: char| c.is_numeric()).collect();
/// assert_eq!(v, vec!["abc", "def", "ghi"]);
/// ```

それらのコードブロックの詳細について議論しましょう。

コードブロック注釈

コメント内にRustのコードを書くためには、3連バッククオートを使います。

fn main() { /// ``` /// println!("Hello, world"); /// ``` fn foo() {} }
/// ```
/// println!("Hello, world");
/// ```

もしRustのコードではないものを書きたいのであれば、注釈を追加することができます。

fn main() { /// ```c /// printf("Hello, world\n"); /// ``` fn foo() {} }
/// ```c
/// printf("Hello, world\n");
/// ```

これは、使われている言語が何であるかに応じてハイライトされます。 もし単なるプレーンテキストを書いているのであれば、 text を選択してください。

ここでは正しい注釈を選ぶことが重要です。なぜなら、 rustdoc はそれを興味深い方法で使うからです。それらが実際のコードと不整合を起こさないように、ライブラリクレート内で実際にあなたの例をテストするために使うのです。 もし例の中にCのコードが含まれているのに、あなたが注釈を付けるのを忘れてしまい、 rustdoc がそれをRustのコードだと考えてしまえば、 rustdoc はドキュメントを生成しようとするときに怒るでしょう。

テストとしてのドキュメント

次のようなドキュメントにおける例について議論しましょう。

fn main() { /// ``` /// println!("Hello, world"); /// ``` fn foo() {} }
/// ```
/// println!("Hello, world");
/// ```

fn main() とかがここでは不要だということに気が付くでしょう。 rustdoc は自動的に main() ラッパをコードの周りに、正しい場所へ配置するためのヒューリスティクスを使って追加します。 例えば、こうです。

fn main() { /// ``` /// use std::rc::Rc; /// /// let five = Rc::new(5); /// ``` fn foo() {} }
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```

これが、テストのときには結局こうなります。

fn main() { use std::rc::Rc; let five = Rc::new(5); }
fn main() {
    use std::rc::Rc;
    let five = Rc::new(5);
}

これがrustdocが例の前処理に使うアルゴリズムの全てです。

  1. 前の方にある全ての #![foo] アトリビュートは、そのままクレートのアトリビュートとして置いておく
  2. unused_variablesunused_assignmentsunused_mutunused_attributesdead_code などのいくつかの一般的な allow アトリビュートを追加する。 小さな例はしばしばこれらのリントに引っ掛かる
  3. もしその例が extern crate を含んでいなければ、 extern crate <mycrate>; を挿入する( #[macro_use] がないことに注意する)
  4. 最後に、もし例が fn main を含んでいなければ、テキストの残りの部分を fn main() { your_code } で囲む

こうして生成された fn main は問題になり得ます! もし use 文によって参照される例のコードに extern crate 文や mod 文が入っていれば、それらはステップ4を抑制するために少なくとも fn main() {} を含んでいない限り失敗します。 #[macro_use] extern crate も同様に、クレートのルート以外では動作しません。そのため、マクロのテストには明示的な main が常に必要なのです。 しかし、ドキュメントを散らかす必要はありません……続きを読みましょう!

しかし、これでは不十分なことがときどきあります。 例えば、今まで話してきた全ての /// の付いたコード例はどうだったでしょうか。 生のテキストはこうなっています。

/// 何らかのドキュメント
# fn foo() {}

それは出力とは違って見えます。

fn main() { /// Some documentation. /// 何らかのドキュメント fn foo() {} }
/// 何らかのドキュメント

そうです。正解です。 # で始まる行を追加することで、コードをコンパイルするときには使われるけれども、出力はされないというようにすることができます。 これは都合のよいように使うことができます。 この場合、ドキュメンテーションコメントそのものを見せたいので、ドキュメンテーションコメントを何らかの関数に適用する必要があります。そのため、その後に小さい関数定義を追加する必要があります。 同時に、それは単にコンパイラを満足させるためだけのものなので、それを隠すことで、例がすっきりするのです。 長い例を詳細に説明する一方、テスト可能性を維持するためにこのテクニックを使うことができます。

例えば、このコードをドキュメントに書きたいとします。

fn main() { let x = 5; let y = 6; println!("{}", x + y); }
let x = 5;
let y = 6;
println!("{}", x + y);

最終的にはこのように見えるドキュメントが欲しいのかもしれません。

まず、xに5をセットする

fn main() { let x = 5; let y = 6; println!("{}", x + y); }
let x = 5;

次に、yに6をセットする

fn main() { let x = 5; let y = 6; println!("{}", x + y); }
let y = 6;

最後に、xyとの合計を出力する

fn main() { let x = 5; let y = 6; println!("{}", x + y); }
println!("{}", x + y);

各コードブロックをテスト可能な状態にしておくために、各ブロックにはプログラム全体が必要です。しかし、読者には全ての行を毎回見せたくはありません。 ソースコードに挿入するものはこれです。

    まず、`x`に5をセットする

    ```text
    let x = 5;
    # let y = 6;
    # println!("{}", x + y);
    ```

    次に、`y`に6をセットする

    ```text
    # let x = 5;
    let y = 6;
    # println!("{}", x + y);
    ```

    最後に、`x`と`y`との合計を出力する

    ```text
    # let x = 5;
    # let y = 6;
    println!("{}", x + y);
    ```

例の全体を繰り返すことで、例がちゃんとコンパイルされることを保証する一方、説明に関係する部分だけを見せることができます。

マクロのドキュメントの作成

これはマクロのドキュメントの例です。

/// Panic with a given message unless an expression evaluates to true. /// 式がtrueと評価されない限り、与えられたメッセージとともにパニックする /// /// # Examples /// /// ``` /// # #[macro_use] extern crate foo; /// # fn main() { /// panic_unless!(1 + 1 == 2, “Math is broken.”); /// # } /// ``` /// /// ```should_panic /// # #[macro_use] extern crate foo; /// # fn main() { /// panic_unless!(true == false, “I’m broken.”); /// # } /// ``` #[macro_export] macro_rules! panic_unless { ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } }); } fn main() {}
/// 式がtrueと評価されない限り、与えられたメッセージとともにパニックする
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(1 + 1 == 2, “Math is broken.”);
/// # }
/// ```
///
/// ```should_panic
/// # #[macro_use] extern crate foo;
/// # fn main() {
/// panic_unless!(true == false, “I’m broken.”);
/// # }
/// ```
#[macro_export]
macro_rules! panic_unless {
    ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } });
}

3つのことに気が付くでしょう。 #[macro_use] アトリビュートを追加するために、自分で extern crate 行を追加しなければなりません。 2つ目に、 main() も自分で追加する必要があります(理由は前述しました)。 最後に、それらの2つが出力されないようにコメントアウトするという # の賢い使い方です。

# の使うと便利な場所のもう1つのケースは、エラーハンドリングを無視したいときです。 次のようにしたいとしましょう。

fn main() { /// use std::io; /// let mut input = String::new(); /// try!(io::stdin().read_line(&mut input)); }
/// use std::io;
/// let mut input = String::new(); 
/// try!(io::stdin().read_line(&mut input));

問題は try!Result<T, E> を返すところ、テスト関数は何も返さないことで、これは型のミスマッチエラーを起こします。

fn main() { /// A doc test using try! /// try!を使ったドキュメンテーションテスト /// /// ``` /// use std::io; /// # fn foo() -> io::Result<()> { /// let mut input = String::new(); /// try!(io::stdin().read_line(&mut input)); /// # Ok(()) /// # } /// ``` fn foo() {} }
/// try!を使ったドキュメンテーションテスト
///
/// ```
/// use std::io;
/// # fn foo() -> io::Result<()> {
/// let mut input = String::new(); 
/// try!(io::stdin().read_line(&mut input));
/// # Ok(())
/// # }
/// ```

これは関数内のコードをラッピングすることで回避できます。 これはドキュメント上のテストが実行されるときに Result<T, E> を捕まえて飲み込みます。 このパターンは標準ライブラリ内でよく現れます。

ドキュメンテーションテストの実行

テストを実行するには、次のどちらかを使います。

$ rustdoc --test path/to/my/crate/root.rs
# or
$ cargo test

正解です。 cargo test は組み込まれたドキュメントもテストします。 しかし、cargo testがテストするのはライブラリクレートだけで、バイナリクレートはテストしません。 これは rustdoc の動き方によるものです。それはテストするためにライブラリをリンクしますが、バイナリには何もリンクするものがないからです。

rustdoc がコードをテストするときに正しく動作するのを助けるために便利な注釈があと少しあります。

fn main() { /// ```ignore /// fn foo() { /// ``` fn foo() {} }
/// ```ignore
/// fn foo() {
/// ```

ignore ディレクティブはRustにコードを無視するよう指示します。 これはあまりに汎用的なので、必要になることはほとんどありません。 もしそれがコードではなければ、代わりに text の注釈を付けること、又は問題となる部分だけが表示された、動作する例を作るために # を使うことを検討してください。

fn main() { /// ```should_panic /// assert!(false); /// ``` fn foo() {} }
/// ```should_panic
/// assert!(false);
/// ```

should_panic は、そのコードは正しくコンパイルされるべきではあるが、実際にテストとして成功する必要まではないということを rustdoc に教えます。

fn main() { /// ```no_run /// loop { /// println!("Hello, world"); /// } /// ``` fn foo() {} }
/// ```no_run
/// loop {
///     println!("Hello, world");
/// }
/// ```

no_run アトリビュートはコードをコンパイルしますが、実行はしません。 これは「これはネットワークサービスを開始する方法です」というような例や、コンパイルされることは保証したいけれども、無限ループになってしまうような例にとって重要です!

モジュールのドキュメントの作成

Rustには別の種類のドキュメンテーションコメント、 //! があります。 このコメントは次に続く要素のドキュメントではなく、それを囲っている要素のドキュメントです。 言い換えると、こうです。

fn main() { mod foo { //! This is documentation for the `foo` module. //! これは`foo`モジュールのドキュメントである //! //! # Examples // ... } }
mod foo {
    //! これは`foo`モジュールのドキュメントである
    //!
    //! # Examples

    // ...
}

//! を頻繁に見る場所がここ、モジュールドキュメントです。 もし foo.rs 内にモジュールを持っていれば、しばしばそのコードを開くとこれを見るでしょう。

fn main() { //! A module for using `foo`s. //! `foo`で使われるモジュール //! //! The `foo` module contains a lot of useful functionality blah blah blah //! `foo`モジュールに含まれているたくさんの便利な関数などなど }
//! `foo`で使われるモジュール
//!
//! `foo`モジュールに含まれているたくさんの便利な関数などなど

ドキュメンテーションコメントのスタイル

ドキュメントのスタイルや書式についての全ての慣習を知るには RFC 505 をチェックしてください。

その他のドキュメント

ここにある振舞いは全て、Rust以外のソースコードファイルでも働きます。 コメントはMarkdownで書かれるので、しばしば .md ファイルになります。

ドキュメントをMarkdownファイルに書くとき、ドキュメントにコメントのプレフィックスを付ける必要はありません。 例えば、こうする必要はありません。

fn main() { /// # Examples /// /// ``` /// use std::rc::Rc; /// /// let five = Rc::new(5); /// ``` fn foo() {} }
/// # Examples
///
/// ```
/// use std::rc::Rc;
///
/// let five = Rc::new(5);
/// ```

これは、単にこうします。

# Examples

```
use std::rc::Rc;

let five = Rc::new(5);
```

Markdownファイルの中ではこうします。 ただし、1つだけ新しいものがあります。Markdownファイルではこのように題名を付けなければなりません。

# % The title
% タイトル

# This is the example documentation.
これはサンプルのドキュメントです。

この % 行はそのファイルの一番先頭の行に書く必要があります。

doc アトリビュート

もっと深いレベルで言えは、ドキュメンテーションコメントはドキュメントアトリビュートの糖衣構文です。

fn main() { /// this fn foo() {} #[doc="this"] fn bar() {} }
/// this

#[doc="this"]

これらは同じもので、次のものも同じものです。

fn main() { //! this #![doc="this"] }
//! this

#![doc="this"]

このアトリビュートがドキュメントを書くために使われているのを見ることはそんなにないでしょう。しかし、これは何らかのオプションを変更したり、マクロを書いたりするときに便利です。

再エクスポート

rustdoc はパブリックな再エクスポートがなされた場合に、両方の場所にドキュメントを表示します。

fn main() { extern crate foo; pub use foo::bar; }
extern crate foo;

pub use foo::bar;

これは bar のドキュメントをクレートのドキュメントの中に生成するのと同様に、 foo クレートのドキュメントの中にも生成します。 同じドキュメントが両方の場所で使われます。

この振舞いは no_inline で抑制することができます。

fn main() { extern crate foo; #[doc(no_inline)] pub use foo::bar; }
extern crate foo;

#[doc(no_inline)]
pub use foo::bar;

ドキュメントの不存在

ときどき、プロジェクト内の公開されている全てのものについて、ドキュメントが作成されていることを確認したいことがあります。これは特にライブラリについて作業をしているときにあります。 Rustでは、要素にドキュメントがないときに警告やエラーを生成することができます。 警告を生成するためには、 warn を使います。

fn main() { #![warn(missing_docs)] }
#![warn(missing_docs)]

そしてエラーを生成するとき、 deny を使います。

fn main() { #![deny(missing_docs)] }
#![deny(missing_docs)]

何かを明示的にドキュメント化されていないままにするため、それらの警告やエラーを無効にしたい場合があります。 これは allow を使えば可能です。

fn main() { #[allow(missing_docs)] struct Undocumented; }
#[allow(missing_docs)]
struct Undocumented;

ドキュメントから要素を完全に見えなくしたいこともあるかもしれません。

fn main() { #[doc(hidden)] struct Hidden; }
#[doc(hidden)]
struct Hidden;

HTMLの制御

rustdoc の生成するHTMLのいくつかの外見は、 #![doc] アトリビュートを通じて制御することができます。

fn main() { #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/")] }
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://doc.rust-lang.org/")]

これは、複数の異なったオプション、つまりロゴ、お気に入りアイコン、ルートのURLをセットします。

ドキュメンテーションテストの設定

rustdoc がドキュメントの例をテストする方法は、 #[doc(test(..))] アトリビュートを通じて設定することができます。

fn main() { #![doc(test(attr(allow(unused_variables), deny(warnings))))] }
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

これによって例の中の使われていない値は許されるようになりますが、その他の全てのリントの警告に対してテストは失敗するようになるでしょう。

生成オプション

rustdoc はさらなるカスタマイズのために、その他にもコマンドラインのオプションをいくつか持っています。

セキュリティ上の注意

ドキュメンテーションコメント内のMarkdownは最終的なウェブページの中に無修正で挿入されます。 リテラルのHTMLには注意してください。

fn main() { /// <script>alert(document.cookie)</script> fn foo() {} }
/// <script>alert(document.cookie)</script>