第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) -> Option { /// // 実装 /// } /// } ///
pub 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 まとめ

    学んだこと

  • rustdocの基礎
- /////!の使い分け - 標準セクション(Examples, Panics, Errors, Safety)

  • マークダウン
- 見出し、リスト、表 - コードブロック

  • ドキュメントテスト
- 自動実行される例 - テストの制御(ignore, no_runなど)

  • 公開
- docs.rsでの自動公開 - カスタマイズ

次のステップ

次の章では、Cargoの高度な機能を学びます: