注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
ドキュメントはどんなソフトウェアプロジェクトにとっても重要な部分であり、Rustにおいてはファーストクラスです。 プロジェクトのドキュメントを作成するために、Rustが提供するツールについて話しましょう。
rustdoc
についてRustの配布物には rustdoc
というドキュメントを生成するツールが含まれています。
rustdoc
は cargo 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種類のヘッダが使われます。
今のところそれらは特別な構文ではなく、単なる慣習です。
/// # Panics
Rustにおいて、関数の回復不可能な誤用(つまり、プログラミングエラー)は普通、パニックによって表現されます。パニックは、少なくとも現在のスレッド全体の息の根を止めてしまいます。 もし関数にこのような、パニックによって検出されたり強制されたりするような自明でない取決めがあるときには、ドキュメントを作成することは非常に重要です。
fn main() { /// # Failures fn foo() {} }
/// # Failures
もし関数やメソッドが Result<T, E>
を戻すのであれば、それが Err(E)
を戻したときの状況をドキュメントで説明するのはよいことです。
これは Panics
のときに比べると重要性は少し下です。失敗は型システムによってコード化されますが、それでもまだそうすることはよいことだからです。
/// # Safety
もし関数が unsafe
であれば、呼出元が動作を続けるためにはどの不変条件について責任を持つべきなのかを説明すべきです。
/// # Examples /// /// ``` /// use std::rc::Rc; /// /// let five = Rc::new(5); /// ```
4つ目は Examples
です。
関数やメソッドの使い方の例を1つ以上含めてください。そうすればユーザから愛されることでしょう。
それらの例はコードブロック注釈内に入れます。コードブロック注釈についてはすぐ後で話しますが、それらは1つ以上のセクションを持つことができます。
/// # 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()
ラッパをコードの周りに、正しい場所へ配置するためのヒューリスティクスを使って追加します。
例えば、こうです。
/// ``` /// 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が例の前処理に使うアルゴリズムの全てです。
#![foo]
アトリビュートは、そのままクレートのアトリビュートとして置いておくunused_variables
、 unused_assignments
、 unused_mut
、 unused_attributes
、 dead_code
などのいくつかの一般的な allow
アトリビュートを追加する。
小さな例はしばしばこれらのリントに引っ掛かるextern crate
を含んでいなければ、 extern crate <mycrate>;
を挿入する( #[macro_use]
がないことに注意する)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);
最終的にはこのように見えるドキュメントが欲しいのかもしれません。
まず、
fn main() { let x = 5; let y = 6; println!("{}", x + y); }x
に5をセットするlet x = 5;次に、
fn main() { let x = 5; let y = 6; println!("{}", x + y); }y
に6をセットするlet y = 6;最後に、
fn main() { let x = 5; let y = 6; println!("{}", x + y); }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つのケースは、エラーハンドリングを無視したいときです。
次のようにしたいとしましょう。
/// use std::io; /// let mut input = String::new(); /// try!(io::stdin().read_line(&mut input));
問題は try!
が Result<T, E>
を返すところ、テスト関数は何も返さないことで、これは型のミスマッチエラーを起こします。
/// 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
がコードをテストするときに正しく動作するのを助けるために便利な注釈があと少しあります。
/// ```ignore /// fn foo() { /// ```
ignore
ディレクティブはRustにコードを無視するよう指示します。
これはあまりに汎用的なので、必要になることはほとんどありません。
もしそれがコードではなければ、代わりに text
の注釈を付けること、又は問題となる部分だけが表示された、動作する例を作るために #
を使うことを検討してください。
/// ```should_panic /// assert!(false); /// ```
should_panic
は、そのコードは正しくコンパイルされるべきではあるが、実際にテストとして成功する必要まではないということを rustdoc
に教えます。
/// ```no_run /// loop { /// println!("Hello, world"); /// } /// ```
no_run
アトリビュートはコードをコンパイルしますが、実行はしません。
これは「これはネットワークサービスを開始する方法です」というような例や、コンパイルされることは保証したいけれども、無限ループになってしまうような例にとって重要です!
Rustには別の種類のドキュメンテーションコメント、 //!
があります。
このコメントは次に続く要素のドキュメントではなく、それを囲っている要素のドキュメントです。
言い換えると、こうです。
mod foo { //! これは`foo`モジュールのドキュメントである //! //! # Examples // ... }
//!
を頻繁に見る場所がここ、モジュールドキュメントです。
もし foo.rs
内にモジュールを持っていれば、しばしばそのコードを開くとこれを見るでしょう。
//! `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
はパブリックな再エクスポートがなされた場合に、両方の場所にドキュメントを表示します。
extern crate foo; pub use foo::bar;
これは bar
のドキュメントをクレートのドキュメントの中に生成するのと同様に、 foo
クレートのドキュメントの中にも生成します。
同じドキュメントが両方の場所で使われます。
この振舞いは no_inline
で抑制することができます。
extern crate foo; #[doc(no_inline)] pub use foo::bar;
ときどき、プロジェクト内の公開されている全てのものについて、ドキュメントが作成されていることを確認したいことがあります。これは特にライブラリについて作業をしているときにあります。
Rustでは、要素にドキュメントがないときに警告やエラーを生成することができます。
警告を生成するためには、 warn
を使います。
#![warn(missing_docs)]
そしてエラーを生成するとき、 deny
を使います。
#![deny(missing_docs)]
何かを明示的にドキュメント化されていないままにするため、それらの警告やエラーを無効にしたい場合があります。
これは allow
を使えば可能です。
#[allow(missing_docs)] struct Undocumented;
ドキュメントから要素を完全に見えなくしたいこともあるかもしれません。
fn main() { #[doc(hidden)] struct Hidden; }#[doc(hidden)] struct Hidden;
rustdoc
の生成するHTMLのいくつかの外見は、 #![doc]
アトリビュートを通じて制御することができます。
#![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(..))]
アトリビュートを通じて設定することができます。
#![doc(test(attr(allow(unused_variables), deny(warnings))))]
これによって例の中の使われていない値は許されるようになりますが、その他の全てのリントの警告に対してテストは失敗するようになるでしょう。
rustdoc
はさらなるカスタマイズのために、その他にもコマンドラインのオプションをいくつか持っています。
--html-in-header FILE
: FILEの内容を <head>...</head>
セクションの末尾に加える--html-before-content FILE
: FILEの内容を <body>
の直後、レンダリングされた内容(検索バーを含む)の直前に加える--html-after-content FILE
: FILEの内容を全てのレンダリングされた内容の後に加えるドキュメンテーションコメント内のMarkdownは最終的なウェブページの中に無修正で挿入されます。 リテラルのHTMLには注意してください。
fn main() { /// <script>alert(document.cookie)</script> fn foo() {} }
/// <script>alert(document.cookie)</script>