Crates.ioにクレートを公開する

プロジェクトの依存としてcrates.ioのパッケージを使用しましたが、 自分のパッケージを公開することで他の人とコードを共有することもできます。 crates.ioのクレート登録所は、自分のパッケージのソースコードを配布するので、 主にオープンソースのコードをホストします。

RustとCargoは、公開したパッケージを人が使用し、そもそも見つけやすくしてくれる機能を有しています。 これらの機能の一部を次に語り、そして、パッケージの公開方法を説明します。

役に立つドキュメンテーションコメントを行う

パッケージを正確にドキュメントすることで、他のユーザがパッケージを使用する方法や、いつ使用すべきかを理解する手助けをすることになるので、 ドキュメンテーションを書くことに時間を費やす価値があります。第3章で、2連スラッシュ、//でRustのコードにコメントをつける方法を議論しました。 Rustには、ドキュメンテーション用のコメントも用意されていて、便利なことにドキュメンテーションコメントとして知られ、 HTMLドキュメントを生成します。クレートの実装法とは対照的にクレートの使用法を知ることに興味のあるプログラマ向けの、 公開API用のドキュメンテーションコメントの中身をこのHTMLは表示します。

ドキュメンテーションコメントは、2つではなく、3連スラッシュ、///を使用し、テキストを整形するMarkdown記法もサポートしています。 ドキュメント対象の要素の直前にドキュメンテーションコメントを配置してください。 リスト14-1は、my_crateという名のクレートのadd_one関数用のドキュメンテーションコメントを示しています:

ファイル名: src/lib.rs

/// Adds one to the number given.
/// 与えられた数値に1を足す。
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

リスト14-1: 関数のドキュメンテーションコメント

ここで、add_one関数がすることの説明を与え、Examplesというタイトルでセクションを開始し、 add_one関数の使用法を模擬するコードを提供しています。このドキュメンテーションコメントからcargo docを実行することで、 HTMLドキュメントを生成することができます。このコマンドはコンパイラとともに配布されているrustdocツールを実行し、 生成されたHTMLドキュメントをtarget/docディレクトリに配置します。

利便性のために、cargo doc --openを走らせれば、現在のクレートのドキュメント用のHTML(と、 自分のクレートが依存している全てのドキュメント)を構築し、その結果をWebブラウザで開きます。 add_one関数まで下り、図14-1に示したように、ドキュメンテーションコメントのテキストがどう描画されるかを確認しましょう:

`my_crate`の`add_one`関数の描画済みのHTMLドキュメント

図14-1: add_one関数のHTMLドキュメント

よく使われるセクション

# Examplesマークダウンのタイトルをリスト14-1で使用し、「例」というタイトルのセクションをHTMLに生成しました。 こちらがこれ以外にドキュメントでよくクレート筆者が使用するセクションです:

  • Panics: ドキュメント対象の関数がpanic!する可能性のある筋書きです。プログラムをパニックさせたくない関数の使用者は、 これらの状況で関数が呼ばれないことを確かめる必要があります。
  • Errors: 関数がResultを返すなら、起きうるエラーの種類とどんな条件がそれらのエラーを引き起こす可能性があるのか解説すると、 呼び出し側の役に立つので、エラーの種類によって処理するコードを変えて書くことができます。
  • Safety: 関数が呼び出すのにunsafe(unsafeについては第19章で議論します)なら、 関数がunsafeな理由を説明し、関数が呼び出し元に保持していると期待する不変条件を講義するセクションがあるべきです。

多くのドキュメンテーションコメントでは、これら全てのセクションが必要になることはありませんが、 これは自分のコードを呼び出している人が知りたいと思うコードの方向性を思い出させてくれるいいチェックリストになります。

テストとしてのドキュメンテーションコメント

ドキュメンテーションコメントに例のコードブロックを追加すると、ライブラリの使用方法のデモに役立ち、 おまけもついてきます: cargo testを走らせると、ドキュメントのコード例をテストとして実行するのです! 例付きのドキュメントに上回るものはありません。しかし、ドキュメントが書かれてからコードが変更されたがために、 動かない例がついているよりも悪いものもありません。リスト14-1からadd_one関数のドキュメンテーションとともに、 cargo testを走らせたら、テスト結果に以下のような区域が見られます:

   Doc-tests my_crate

running 1 test
test src/lib.rs - add_one (line 5) ... ok

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

さて、例のassert_eq!がパニックするように、関数か例を変更し、再度cargo testを実行したら、 docテストが、例とコードがお互いに同期されていないことを捕捉するところを目撃するでしょう!

含まれている要素にコメントする

docコメントの別スタイル、//!は、コメントに続く要素にドキュメンテーションを付け加えるのではなく、 コメントを含む要素にドキュメンテーションを付け加えます。典型的には、クレートのルートファイル(慣例的には、src/lib.rs)内部や、 モジュールの内部で使用して、クレートやモジュール全体にドキュメントをつけます。

例えば、add_one関数を含むmy_crateクレートの目的を解説するドキュメンテーションを追加したいのなら、 //!で始まるドキュメンテーションコメントをsrc/lib.rsファイルの先頭につけることができます。 リスト14-2に示したようにですね:

ファイル名: src/lib.rs

//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.

//! #自分のクレート
//!
//! `my_crate`は、ユーティリティの集まりであり、特定の計算をより便利に行うことができます。

/// Adds one to the number given.
// --snip--

リスト14-2: 全体としてmy_crateクレートにドキュメントをつける

//!で始まる最後の行のあとにコードがないことに注目してください。///ではなく、//!でコメントを開始しているので、 このコメントに続く要素ではなく、このコメントを含む要素にドキュメントをつけているわけです。 今回の場合、このコメントを含む要素はsrc/lib.rsファイルであり、クレートのルートです。 これらのコメントは、クレート全体を解説しています。

cargo doc --openを実行すると、これらのコメントは、my_crateのドキュメントの最初のページ、 クレートの公開要素のリストの上部に表示されます。図14-2のようにですね:

クレート全体のコメント付きの描画済みHTMLドキュメンテーション

図14-2: クレート全体を解説するコメントを含むmy_crateの描画されたドキュメンテーション

要素内のドキュメンテーションコメントは、特にクレートやモジュールを解説するのに有用です。 コンテナの全体の目的を説明し、クレートの使用者がクレートの体系を理解する手助けをするのに使用してください。

pub useで便利な公開APIをエクスポートする

第7章において、modキーワードを使用してモジュールにコードを体系化する方法、pubキーワードで要素を公開にする方法、 useキーワードで要素をスコープに導入する方法について講義しました。しかしながら、クレートの開発中に、 自分にとって意味のある構造は、ユーザにはあまり便利ではない可能性があります。複数階層を含む階層で、 自分の構造体を体系化したくなるかもしれませんが、それから階層の深いところで定義した型を使用したい人は、 型が存在することを見つけ出すのに困難を伴う可能性もあります。また、そのような人は、 use my_crate::UsefulTypeの代わりにuse my_crate::some_module::another_module::UsefulType;と入力するのを煩わしく感じる可能性もあります。

自分の公開APIの構造は、クレートを公開する際に考慮すべき点です。自分のクレートを使用したい人は、 自分よりもその構造に馴染みがないですし、クレートのモジュール階層が大きければ、使用したい部分を見つけるのが困難になる可能性があります。

嬉しいお知らせは、構造が他人が他のライブラリから使用するのに便利ではない場合、内部的な体系を再構築する必要はないということです: 代わりに、要素を再エクスポートし、pub useで自分の非公開構造とは異なる公開構造にできます。 再エクスポートは、ある場所の公開要素を一つ取り、別の場所で定義されているかのように別の場所で公開します。

例えば、芸術的な概念をモデル化するためにartという名のライブラリを作ったとしましょう。 このライブラリ内には、2つのモジュールがあります: PrimaryColorSecondaryColorという名前の2つのenumを含む、 kindsモジュールとmixという関数を含むutilsモジュールです。リスト14-3のようにですね:

ファイル名: src/lib.rs

//! # Art
//!
//! A library for modeling artistic concepts.
//! #芸術
//!
//! 芸術的な概念をモデル化するライブラリ。

pub mod kinds {
    /// The primary colors according to the RYB color model.
    /// RYBカラーモデルによる主色
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// The secondary colors according to the RYB color model.
    /// RYBカラーモデルによる副色
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    use kinds::*;

    /// Combines two primary colors in equal amounts to create
    /// a secondary color.
    ///2つの主色を同じ割合で混合し、副色にする
    pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
        // --snip--
    }
}

リスト14-3: kindsutilsモジュールに体系化される要素を含むartライブラリ

図14-3は、cargo docにより生成されるこのクレートのドキュメンテーションの最初のページがどんな見た目になるか示しています:

`kinds`と`utils`モジュールを列挙する`art`クレートの描画されたドキュメンテーション

図14-3: kindsutilsモジュールを列挙するartのドキュメンテーションのトップページ

PrimaryColor型もSecondaryColor型も、mix関数もトップページには列挙されていないことに注意してください。 kindsutilsをクリックしなければ、参照することができません。

このライブラリに依存する別のクレートは、現在定義されているモジュール構造を指定して、 artの要素をインポートするuse文が必要になるでしょう。リスト14-4は、 artクレートからPrimaryColormix要素を使用するクレートの例を示しています:

ファイル名: src/main.rs

extern crate art;

use art::kinds::PrimaryColor;
use art::utils::mix;

fn main() {
    let red = PrimaryColor::Red;
    let yellow = PrimaryColor::Yellow;
    mix(red, yellow);
}

リスト14-4: 内部構造がエクスポートされてartクレートの要素を使用するクレート

リスト14-4はartクレートを使用していますが、このコードの筆者は、PrimaryColorkindsモジュールにあり、 mixutilsモジュールにあることを理解しなければなりませんでした。artクレートのモジュール構造は、 artクレートの使用者よりも、artクレートに取り組む開発者などに関係が深いです。 クレートの一部をkindsモジュールとutilsモジュールに体系化する内部構造は、artクレートの使用方法を理解しようとする人には、 何も役に立つ情報を含んでいません。代わりに、開発者がどこを見るべきか計算する必要があるので、 artクレートのモジュール構造は混乱を招き、また、開発者はモジュール名をuse文で指定しなければならないので、 この構造は不便です。

公開APIから内部体系を除去するために、リスト14-3のartクレートコードを変更し、pub use文を追加して、 最上位で要素を再エクスポートすることができます。リスト14-5みたいにですね:

ファイル名: src/lib.rs

//! # Art
//!
//! A library for modeling artistic concepts.

pub use kinds::PrimaryColor;
pub use kinds::SecondaryColor;
pub use utils::mix;

pub mod kinds {
    // --snip--
}

pub mod utils {
    // --snip--
}

リスト14-5: pub use文を追加して要素を再エクスポートする

このクレートに対してcargo docが生成するAPIドキュメンテーションは、これで図14-4のようにトップページに再エクスポートを列挙しリンクするので、 PrimaryColor型とSecondaryColor型とmix関数を見つけやすくします。

トップページに再エクスポートのある`art`クレートの描画されたドキュメンテーション

図14-4: 再エクスポートを列挙するartのドキュメンテーションのトップページ

artクレートのユーザは、それでも、リスト14-4にデモされているように、リスト14-3の内部構造を見て使用することもできますし、 リスト14-5のより便利な構造を使用することもできます。リスト14-6に示したようにですね:

ファイル名: src/main.rs

extern crate art;

use art::PrimaryColor;
use art::mix;

fn main() {
    // --snip--
}

リスト14-6: artクレートの再エクスポートされた要素を使用するプログラム

ネストされたモジュールがたくさんあるような場合、最上位階層でpub useにより型を再エクスポートすることは、 クレートの使用者の経験に大きな違いを生みます。

役に立つAPI構造を作ることは、科学というよりも芸術の領域であり、ユーザにとって何が最善のAPIなのか、 探究するために繰り返してみることができます。pub useは、内部的なクレート構造に柔軟性をもたらし、 その内部構造をユーザに提示する構造から切り離してくれます。インストールしてある他のクレートを見て、 内部構造が公開APIと異なっているか確認してみてください。

Crates.ioのアカウントをセットアップする

クレートを公開する前に、crates.ioのアカウントを作成し、 APIトークンを取得する必要があります。そうするには、crates.ioのホームページを訪れ、 Githubアカウントでログインしてください。(現状は、Githubアカウントがなければなりませんが、 いずれは他の方法でもアカウントを作成できるようになる可能性があります。)ログインしたら、 https://crates.io/me/で自分のアカウントの設定に行き、 APIキーを取り扱ってください。そして、cargo loginコマンドをAPIキーとともに実行してください。 以下のようにですね:

$ cargo login abcdefghijklmnopqrstuvwxyz012345

このコマンドは、CargoにAPIトークンを知らせ、~/.cargo/credentialsにローカルに保存します。 このトークンは、秘密です: 他人とは共有しないでください。なんらかの理由で他人と実際に共有してしまったら、 古いものを破棄してcrates.ioで新しいトークンを生成するべきです。

新しいクレートにメタデータを追加する

アカウントはできたので、公開したいクレートがあるとしましょう。公開前に、 Cargo.tomlファイルの[package]セクションに追加することでクレートにメタデータを追加する必要があるでしょう。

クレートには、独自の名前が必要でしょう。クレートをローカルで作成している間、 クレートの名前はなんでもいい状態でした。ところが、crates.ioのクレート名は、 最初に来たもの勝ちの精神で付与されていますので、一旦クレート名が取られてしまったら、 その名前のクレートを他の人が公開することは絶対できません。もう使われているか、 サイトで使いたい名前を検索してください。まだなら、Cargo.tomlファイルの[package]以下の名前を編集して、 名前を公開用に使ってください。以下のように:

ファイル名: Cargo.toml

[package]
name = "guessing_game"

たとえ、独自の名前を選択していたとしても、この時点でcargo publishを実行すると、警告とエラーが出ます:

$ cargo publish
    Updating registry `https://github.com/rust-lang/crates.io-index`
warning: manifest has no description, license, license-file, documentation,
homepage or repository.
(警告: マニフェストに説明、ライセンス、ライセンスファイル、ドキュメンテーション、ホームページ、
リポジトリがありません)
--snip--
error: api errors: missing or empty metadata fields: description, license.
(エラー: APIエラー: 存在しないメタデータフィールド: description, license)

原因は、大事な情報を一部入れていないからです: 説明とライセンスは、 他の人があなたのクレートは何をし、どんな条件の元で使っていいのかを知るために必要なのです。 このエラーを解消するには、Cargo.tomlファイルにこの情報を入れ込む必要があります。

1文か2文程度の説明をつけてください。これは、検索結果に表示されますからね。 licenseフィールドには、ライセンス識別子を与える必要があります。 Linux団体のSoftware Package Data Exchange(SPDX)に、この値に使用できる識別子が列挙されています。 例えば、自分のクレートをMITライセンスでライセンスするためには、 MIT識別子を追加してください:

ファイル名: Cargo.toml

[package]
name = "guessing_game"
license = "MIT"

SPDXに出現しないライセンスを使用したい場合、そのライセンスをファイルに配置し、 プロジェクトにそのファイルを含め、それからlicenseキーを使う代わりに、 そのファイルの名前を指定するのにlicense-fileを使う必要があります。

どのライセンスが自分のプロジェクトに()(さわ)しいかというガイドは、 この本の範疇を超えています。Rustコミュニティの多くの人間は、MIT OR Apache-2.0のデュアルライセンスを使用することで、 Rust自体と同じようにプロジェクトをライセンスします。この実践は、ORで区切られる複数のライセンス識別子を指定して、 プロジェクトに複数のライセンスを持たせることもできることを模擬しています。

独自の名前、バージョン、クレート作成時にcargo newが追加した筆者の詳細、説明、ライセンスが追加され、 公開準備のできたプロジェクト用のCargo.tomlファイルは以下のような見た目になっていることでしょう:

ファイル名: Cargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A fun game where you guess what number the computer has chosen."
              (コンピュータが選択した数字を言い当てる面白いゲーム)
license = "MIT OR Apache-2.0"

[dependencies]

Cargoのドキュメンテーションには、 指定して他人が発見し、より容易くクレートを使用できることを保証する他のメタデータが解説されています。

Crates.ioに公開する

アカウントを作成し、APIトークンを保存し、クレートの名前を決め、必要なメタデータを指定したので、 公開する準備が整いました!クレートを公開すると、特定のバージョンが、 crates.ioに他の人が使用できるようにアップロードされます。

公開は永久なので、クレートの公開時には気をつけてください。バージョンは絶対に上書きできず、 コードも削除できません。crates.ioの一つの主な目標が、 crates.ioのクレートに依存している全てのプロジェクトのビルドが、 動き続けるようにコードの永久アーカイブとして機能することなのです。バージョン削除を可能にしてしまうと、 その目標を達成するのが不可能になってしまいます。ですが、公開できるクレートバージョンの数に制限はありません。

再度cargo publishコマンドを実行してください。今度は成功するはずです:

$ cargo publish
 Updating registry `https://github.com/rust-lang/crates.io-index`
Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
 Finished dev [unoptimized + debuginfo] target(s) in 0.19 secs
Uploading guessing_game v0.1.0 (file:///projects/guessing_game)

おめでとうございます!Rustコミュニティとコードを共有し、誰でもあなたのクレートを依存として簡単に追加できます。

既存のクレートの新バージョンを公開する

クレートに変更を行い、新バージョンをリリースする準備ができたら、 Cargo.tomlファイルに指定されたversionの値を変更し、再公開します。 セマンティックバージョンルールを使用して加えた変更の種類に基づいて次の適切なバージョン番号を決定してください。 そして、cargo publishを実行し、新バージョンをアップロードします。

cargo yankでCrates.ioからバージョンを削除する

以前のバージョンのクレートを削除することはできないものの、将来のプロジェクトがこれに新たに依存することを防ぐことはできます。 これは、なんらかの理由により、クレートバージョンが壊れている場合に有用です。そのような場面において、 Cargoはクレートバージョンの 取り下げ(yank) をサポートしています。

バージョンを取り下げると、既存のプロジェクトは、引き続きダウンロードしたりそのバージョンに依存したりしつづけられますが、 新規プロジェクトが新しくそのバージョンに依存しだすことは防止されます。つまるところ、取り下げは、 すでにCargo.lockが存在するプロジェクトは壊さないが、将来的に生成されたCargo.lockファイルは 取り下げられたバージョンを使わない、ということを意味します。

あるバージョンのクレートを取り下げるには、cargo yankを実行し、取り下げたいバージョンを指定します:

$ cargo yank --vers 1.0.1

--undoをコマンドに付与することで、取り下げを取り消し、再度あるバージョンにプロジェクトを依存させ始めることもできます:

$ cargo yank --vers 1.0.1 --undo

取り下げは、コードの削除は一切しません。例として、取り下げ機能は、誤ってアップロードされた秘密鍵を削除するためのものではありません。 もしそうなってしまったら、即座に秘密鍵をリセットしなければなりません。