第21章: ドキュメンテーション - コードを説明する技術
学習目標
- rustdocの基本的な使い方を習得する
- マークダウンを使った効果的なドキュメントを書く
- ドキュメントテストの活用方法を学ぶ
- APIドキュメントのベストプラクティスを理解する
- ドキュメントの公開方法を学ぶ
---
21.1 なぜドキュメントが重要か
21.1.1 ドキュメントの価値
┌─────────────────────────────────────────────────────────┐
│ 良いドキュメントの価値 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. オンボーディング 新しいメンバーの学習時間短縮 │
│ 2. メンテナンス性 将来の自分への説明 │
│ 3. API発見性 使い方が明確 │
│ 4. バグ削減 期待される動作が明示 │
│ 5. コミュニティ貢献 OSSプロジェクトの成長 │
│ │
└─────────────────────────────────────────────────────────┘
統計データ:
- 開発者はコードを読む時間が書く時間の10倍
- 良いドキュメントは学習時間を50%削減
- ドキュメント化されたAPIは採用率が3倍
21.1.2 Rustのドキュメントの特徴
ビルトインツール:
# ドキュメント生成
cargo doc --open
# ドキュメントテスト実行
cargo test --doc
コードと同期:
- ドキュメントテストがコンパイルされる
- 例が古くなると自動的に失敗
統一されたフォーマット:
- すべてのRustクレートが同じスタイル
- docs.rsで自動公開
---
21.2 基本的なドキュメントコメント
21.2.1 3種類のコメント
// 行コメント(ドキュメント化されない)
/// アウターアイテムドキュメント
/// 関数、構造体、モジュールなどに付ける
pub fn my_function() {}
//! インナーアイテムドキュメント
//! モジュールやクレートの説明に使う
// src/lib.rs
//! このクレートは電卓機能を提供します。
//!
//! # Examples
//!
//!
//! use my_calculator::Calculator;
//!
//! let calc = Calculator::new();
//! 21.2.2 基本的な構造
/// 2つの数を加算します。
///
/// この関数は任意の2つの整数を受け取り、その合計を返します。
/// オーバーフローはチェックされません。
///
/// # Arguments
///
/// * `a` - 最初の数
/// * `b` - 2番目の数
///
/// # Examples
///
///
/// use my_crate::add;
///
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ///
/// # Returns
///
/// 2つの引数の合計を返します。
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
---
21.3 標準セクション
21.3.1 Examples セクション
最も重要なセクション:
/// ユーザーを作成します。
///
/// # Examples
///
/// 基本的な使い方:
///
///
/// use my_app::User;
///
/// let user = User::new("Alice", 30);
/// assert_eq!(user.name(), "Alice");
/// ///
/// エラーハンドリング:
///
/// should_panic
/// use my_app::User;
///
/// // 年齢が負の場合はパニック
/// let user = User::new("Bob", -1);
/// pub fn new(name: &str, age: i32) -> Self {
assert!(age >= 0, "年齢は0以上である必要があります");
User {
name: name.to_string(),
age,
}
}
21.3.2 Panics セクション
/// 配列から要素を取得します。
///
/// # Panics
///
/// インデックスが範囲外の場合、この関数はパニックします。
///
/// should_panic
/// let arr = vec![1, 2, 3];
/// get_element(&arr, 10); // パニック!
/// pub fn get_element<T>(arr: &[T], index: usize) -> &T {
&arr[index]
}
21.3.3 Errors セクション
/// ファイルからユーザーデータを読み込みます。
///
/// # Errors
///
/// この関数は以下の場合にエラーを返します:
///
/// * ファイルが存在しない場合
/// * ファイルの読み込み権限がない場合
/// * JSONのパースに失敗した場合
///
/// # Examples
///
/// no_run
/// use my_app::load_user;
///
/// match load_user("user.json") {
/// Ok(user) => println!("Loaded: {}", user.name),
/// Err(e) => eprintln!("Error: {}", e),
/// }
/// pub fn load_user(path: &str) -> Result<User, std::io::Error> {
let content = std::fs::read_to_string(path)?;
let user: User = serde_json::from_str(&content)?;
Ok(user)
}
21.3.4 Safety セクション
/// ポインタから値を読み込みます。
///
/// # Safety
///
/// この関数は以下の条件が満たされる場合のみ安全です:
///
/// * `ptr`が有効なメモリ領域を指している
/// * `ptr`がアライメントされている
/// * `ptr`が指す値が初期化されている
///
/// # Examples
///
///
/// let x = 42;
/// let ptr = &x as const i32;
///
/// unsafe {
/// let value = read_value(ptr);
/// assert_eq!(value, 42);
/// }
/// pub unsafe fn read_value<T>(ptr: *const T) -> T {
std::ptr::read(ptr)
}
---
21.4 マークダウン記法
21.4.1 基本的なフォーマット
/// # 見出し1
///
/// ## 見出し2
///
/// ### 見出し3
///
/// **太字**、*斜体*、`コード`
///
/// リスト:
/// - 項目1
/// - 項目2
/// - サブ項目
///
/// 番号付きリスト:
/// 1. 最初
/// 2. 次
/// 3. 最後
///
/// リンク:[Rust](https://www.rust-lang.org/)
///
/// 引用:
/// > これは引用です。
/// > 複数行も可能です。
21.4.2 コードブロック
/// コードブロックの例:
///
/// rust
/// fn main() {
/// println!("Hello, world!");
/// }
/// ///
/// 他の言語も指定可能:
///
/// python
/// def hello():
/// print("Hello, world!")
/// ///
/// シンタックスハイライトなし:
///
/// text
/// プレーンテキスト
/// 21.4.3 表
/// 対応する型:
///
/// | 型 | サイズ | 範囲 |
/// |---|--------|------|
/// | `i8` | 1バイト | -128 to 127 |
/// | `i16` | 2バイト | -32768 to 32767 |
/// | `i32` | 4バイト | -2^31 to 2^31-1 |
---
21.5 ドキュメントテスト
21.5.1 基本的なドキュメントテスト
/// 2つの数を乗算します。
///
/// # Examples
///
///
/// use my_crate::multiply;
///
/// assert_eq!(multiply(2, 3), 6);
/// assert_eq!(multiply(-1, 5), -5);
/// pub fn multiply(a: i32, b: i32) -> i32 {
a * b
}
実行:
$ cargo test --doc
running 1 test
test src/lib.rs - multiply (line 5) ... ok
21.5.2 テストの制御
コンパイルしない例:
/// # Examples
///
/// ignore
/// // このコードは実行されない
/// let x = undefined_function();
/// コンパイルエラーを期待:
/// # Examples
///
/// compile_fail
/// let x: i32 = "文字列"; // 型エラー
/// 実行しない(コンパイルのみ):
/// # Examples
///
/// no_run
/// // ファイルシステムに依存するコード
/// std::fs::write("file.txt", "data").unwrap();
/// 21.5.3 隠しコード
/// # Examples
///
///
/// # use my_crate::Database;
/// # let db = Database::connect_test();
/// #
/// let users = db.query("SELECT FROM users");
/// assert!(users.len() > 0);
/// #で始まる行はドキュメントには表示されないが、テストでは実行される。
---
21.6 構造体とトレイトのドキュメント
21.6.1 構造体のドキュメント
/// ユーザー情報を表す構造体。
///
/// この構造体はアプリケーション内のユーザーを表現します。
///
/// # Examples
///
///
/// use my_app::User;
///
/// let user = User {
/// id: 1,
/// name: "Alice".to_string(),
/// email: "alice@example.com".to_string(),
/// };
/// pub struct User {
/// ユーザーの一意なID
pub id: u32,
/// ユーザー名(1-50文字)
pub name: String,
/// メールアドレス
pub email: String,
}
impl User {
/// 新しいユーザーを作成します。
///
/// # Arguments
///
/// * `id` - ユーザーID
/// * `name` - ユーザー名
/// * `email` - メールアドレス
///
/// # Examples
///
///
/// use my_app::User;
///
/// let user = User::new(1, "Alice", "alice@example.com");
/// pub fn new(id: u32, name: &str, email: &str) -> Self {
User {
id,
name: name.to_string(),
email: email.to_string(),
}
}
}
21.6.2 トレイトのドキュメント
/// データベース操作を抽象化するトレイト。
///
/// このトレイトを実装することで、異なるデータベースバックエンドを
/// サポートできます。
///
/// # Examples
///
///
/// use my_app::Repository;
///
/// struct MemoryRepository {
/// // フィールド
/// }
///
/// impl Repository for MemoryRepository {
/// fn find(&self, id: u32) -> Optionpub trait Repository {
/// IDでエンティティを検索します。
///
/// # Arguments
///
/// * `id` - エンティティのID
///
/// # Returns
///
/// 見つかった場合は`Some(T)`、見つからない場合は`None`
fn find(&self, id: u32) -> Option<User>;
/// エンティティを保存します。
///
/// # Errors
///
/// 保存に失敗した場合はエラーを返します。
fn save(&mut self, entity: User) -> Result<(), Error>;
}
---
21.7 モジュールとクレートのドキュメント
21.7.1 クレートレベルのドキュメント
// src/lib.rs
//! # My Calculator
//!
//! このクレートは基本的な電卓機能を提供します。
//!
//! ## Features
//!
//! - 四則演算
//! - メモリ機能
//! - 科学計算(オプション)
//!
//! ## Quick Start
//!
//!
//! use my_calculator::Calculator;
//!
//! let mut calc = Calculator::new();
//! let result = calc.add(2.0, 3.0);
//! assert_eq!(result, 5.0);
//! //!
//! ## Installation
//!
//! toml
//! [dependencies]
//! my-calculator = "0.1"
//! //!
//! ## License
//!
//! MIT License
21.7.2 モジュールのドキュメント
// src/math/mod.rs
//! 数学関連の関数を提供するモジュール。
//!
//! このモジュールには以下のサブモジュールがあります:
//!
//! - [`trig`] - 三角関数
//! - [`stats`] - 統計関数
//!
//! # Examples
//!
//!
//! use my_crate::math;
//!
//! let result = math::sqrt(16.0);
//! assert_eq!(result, 4.0);
//!
pub mod trig;
pub mod stats;
/// 平方根を計算します。
pub fn sqrt(x: f64) -> f64 {
x.sqrt()
}
---
21.8 高度なドキュメント機能
21.8.1 リンク
/// [`User`]構造体を作成します。
///
/// [`Repository::save`]を使って保存できます。
///
/// 詳細は[`user`モジュール][user]を参照してください。
///
/// [user]: crate::user
pub fn create_user() -> User {
// 実装
}
リンクの種類:
[User]- 同じクレート内の型[std::vec::Vec]- 外部クレート[method_name]- メソッド[text][link]- カスタムテキスト
21.8.2 警告とヒント
/// この関数は非推奨です。
///
/// # Warning
///
/// **この関数はバージョン2.0で削除される予定です。**
/// 代わりに[`new_function`]を使用してください。
///
/// # Note
///
/// この関数はスレッドセーフではありません。
#[deprecated(since = "1.5.0", note = "Use new_function instead")]
pub fn old_function() {
// 実装
}
21.8.3 バージョン情報
/// この機能はバージョン1.2で追加されました。
///
/// # Version
///
/// Added in: 1.2.0
///
/// # Stability
///
/// This API is stable.
#[doc(cfg(feature = "advanced"))]
pub fn advanced_feature() {
// 実装
}
---
21.9 ドキュメントの生成と公開
21.9.1 ローカルでの生成
# ドキュメント生成
cargo doc
# ブラウザで開く
cargo doc --open
# 依存クレートも含める
cargo doc --no-deps
# プライベートアイテムも含める
cargo doc --document-private-items
21.9.2 docs.rsでの公開
Cargo.toml設定:
[package]
name = "my-crate"
version = "0.1.0"
edition = "2021"
documentation = "https://docs.rs/my-crate"
[package.metadata.docs.rs]
# すべてのフィーチャーを有効化
all-features = true
# ターゲットを指定
targets = ["x86_64-unknown-linux-gnu"]
公開:
# クレートを公開すると自動的にdocs.rsでビルド
cargo publish
21.9.3 カスタムCSS
// src/lib.rs
#![doc(html_root_url = "https://docs.rs/my-crate/0.1.0")]
#![doc(html_logo_url = "https://example.com/logo.png")]
#![doc(html_favicon_url = "https://example.com/favicon.ico")]
---
21.10 ドキュメントのベストプラクティス
21.10.1 良いドキュメントの特徴
1. 明確な例:
// ✅ 良い例
/// ユーザーを検索します。
///
/// # Examples
///
///
/// let user = db.find_user(1).unwrap();
/// assert_eq!(user.name, "Alice");
///
// ❌ 悪い例
/// ユーザーを検索します。
/// (例なし)
2. エラーケースの説明:
// ✅ 良い例
/// # Errors
///
/// - ファイルが見つからない場合
/// - 読み込み権限がない場合
// ❌ 悪い例
/// エラーが発生する可能性があります。
3. パニックの明示:
// ✅ 良い例
/// # Panics
///
/// インデックスが範囲外の場合にパニックします。
// ❌ 悪い例
/// (パニックの可能性について言及なし)
21.10.2 避けるべきパターン
曖昧な説明:
// ❌ 悪い例
/// データを処理します。
pub fn process(data: &[u8]) -> Vec<u8> {
// 何を処理するのか不明
}
// ✅ 良い例
/// バイト列をBase64エンコードします。
///
/// # Examples
///
///
/// let data = b"hello";
/// let encoded = encode(data);
/// assert_eq!(encoded, "aGVsbG8=");
/// pub fn encode(data: &[u8]) -> String {
// 実装
}
21.10.3 チェックリスト
ドキュメントを書く際のチェックリスト:
- [ ] すべての公開関数にドキュメント
- [ ] 少なくとも1つの例
- [ ] エラーケースの説明(該当する場合)
- [ ] パニックの条件(該当する場合)
- [ ] 型パラメータの説明(該当する場合)
- [ ] ドキュメントテストが通る
- [ ] リンクが正しい
---
21.11 ツールとインテグレーション
21.11.1 cargo-docコマンド
# 警告をエラーとして扱う
RUSTDOCFLAGS="-D warnings" cargo doc
# JSONフォーマットで出力
cargo rustdoc -- --output-format json
# 特定のクレートのみ
cargo doc -p my-crate
21.11.2 CI/CDインテグレーション
GitHub Actions:
name: Documentation
on: [push, pull_request]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build documentation
run: cargo doc --no-deps
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/doc
---
21.12 まとめ
学んだこと
///と//!の使い分け
- 標準セクション(Examples, Panics, Errors, Safety)- マークダウン
- ドキュメントテスト
ignore, no_runなど)- 公開
次のステップ
次の章では、Cargoの高度な機能を学びます:
- ワークスペース
- Features
- プロファイル
- The rustdoc book
- RFC 1574 - API Documentation Conventions
- docs.rs
---