Crates.ioにクレートを公開する
私たちはプロジェクトの依存関係としてcrates.ioのパッケージを使用してきましたが、自分自身のパッケージを公開して他の人々とコードを共有することもできます。crates.ioのクレートレジストリは、あなたのパッケージのソースコードを配布するので、主にオープンソースのコードをホストしています。
RustとCargoには、公開したパッケージを人々が見つけやすく、また使いやすくするための機能があります。次にこれらの機能のいくつかについて話し、それからパッケージを公開する方法を説明します。
有用なドキュメンテーションコメントの作成
パッケージを正確にドキュメント化することは、他のユーザーがいつ、どのようにそれらを使用するかを知るのに役立ちます。そのため、ドキュメントの記述に時間をかける価値があります。第3章では、2つのスラッシュ // を使ってRustのコードにコメントする方法を議論しました。Rustにはドキュメンテーションのための特別な種類のコメントもあり、都合の良いことに ドキュメンテーションコメント と呼ばれ、HTMLドキュメントを生成します。このHTMLは、クレートがどのように 実装されているか ではなく、クレートをどのように 使用する かを知りたいプログラマを対象とした、公開APIアイテムのドキュメンテーションコメントの内容を表示します。
ドキュメンテーションコメントは2つではなく3つのスラッシュ /// を使用し、テキストをフォーマットするためにMarkdown記法をサポートします。ドキュメンテーションコメントは、ドキュメント化するアイテムの直前に配置します。リスト14-1は、my_crate という名前のクレート内の add_one 関数のドキュメンテーションコメントを示しています。
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
ここでは、add_one 関数が何をするかの説明を与え、Examples という見出しでセクションを開始し、そして add_one 関数の使用方法を実演するコードを提供しています。このドキュメンテーションコメントからHTMLドキュメントを生成するには、cargo doc を実行します。このコマンドはRustと共に配布されている rustdoc ツールを実行し、生成されたHTMLドキュメントを target/doc ディレクトリに配置します。
利便性のために、cargo doc --open を実行すると、現在のクレートのドキュメント(およびクレートのすべての依存関係のドキュメント)のHTMLをビルドし、その結果をWebブラウザで開きます。add_one 関数に移動すると、図14-1に示すように、ドキュメンテーションコメントのテキストがどのようにレンダリングされるかがわかります。
図14-1: add_one 関数のHTMLドキュメント
一般的に使用されるセクション
リスト14-1では、Markdownの見出し # Examples を使用して、HTMLに「Examples」というタイトルのセクションを作成しました。以下に、クレートの作者がドキュメントで一般的に使用する他のセクションをいくつか挙げます:
- Panics: ドキュメント化されている関数がパニックする可能性のあるシナリオです。プログラムをパニックさせたくない関数の呼び出し元は、これらの状況で関数を呼び出さないようにする必要があります。
- Errors: 関数が
Resultを返す場合、発生する可能性のあるエラーの種類と、それらのエラーが返される原因となる条件を説明することは、呼び出し元がさまざまな種類のエラーをさまざまな方法で処理するコードを書くのに役立ちます。 - Safety: 関数を呼び出すのが
unsafeである場合(unsafe性については第20章で議論します)、なぜその関数が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; finished in 0.27s
ここで、関数か例のいずれかを変更して、例の中の assert_eq! がパニックするようにし、再度 cargo test を実行すると、ドキュメントテストが例とコードが互いに同期していないことを検出するのがわかります!
コンテナアイテムのコメント
//! スタイルのドキュメントコメントは、コメントに続くアイテムではなく、コメントを含むアイテムにドキュメントを追加します。通常、これらのドキュメントコメントは、クレート全体やモジュール全体をドキュメント化するために、クレートルートファイル(慣例的に_src/lib.rs_)やモジュール内部で使用します。
例えば、add_one 関数を含む my_crate クレートの目的を説明するドキュメントを追加するには、リスト14-2に示すように、//! で始まるドキュメンテーションコメントを src/lib.rs ファイルの先頭に追加します。
//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.
/// Adds one to the number given.
// --snip--
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
//! で始まる最後の行の後にコードがないことに注目してください。/// ではなく //! でコメントを開始したため、このコメントに続くアイテムではなく、このコメントを含むアイテムをドキュメント化しています。この場合、そのアイテムはクレートルートである src/lib.rs ファイルです。これらのコメントはクレート全体を説明します。
cargo doc --open を実行すると、これらのコメントは図14-2に示すように、my_crate のドキュメントのフロントページで、クレート内の公開アイテムのリストの上に表示されます。
アイテム内のドキュメンテーションコメントは、特にクレートやモジュールを説明するのに役立ちます。これらを使用してコンテナの全体的な目的を説明し、ユーザーがクレートの構成を理解するのを助けます。
図14-2: クレート全体を説明するコメントを含む、my_crate のレンダリングされたドキュメント
便利な公開APIをエクスポートする
クレートを公開する際、公開APIの構造は主要な考慮事項です。クレートの利用者は、作者ほどその構造に詳しくなく、クレートが大規模なモジュール階層を持つ場合、使いたい部分を見つけるのに苦労するかもしれません。
第7章では、pubキーワードを使ってアイテムを公開する方法と、useキーワードを使ってアイテムをスコープに取り込む方法について説明しました。しかし、クレートを開発している間は理に適っている構造が、利用者にとってあまり便利でないかもしれません。あなたは構造体を複数レベルの階層で整理したいかもしれませんが、その階層の奥深くで定義した型を使いたい人は、その型が存在することを見つけ出すのに苦労するかもしれません。また、use my_crate::UsefulType; ではなく use my_crate::some_module::another_module::UsefulType; と入力しなければならないことにイライラするかもしれません。
良いニュースは、もしその構造が他のライブラリから使うのに便利でなくても、内部の構成を再編成する必要はないということです。代わりに、pub use を使うことでアイテムを再エクスポートし、プライベートな構造とは異なる公開用の構造を作ることができます。再エクスポートは、ある場所にある公開アイテムを取得し、あたかもその別の場所で定義されていたかのように、その場所で公開します。
例えば、芸術的な概念をモデリングするための art という名前のライブラリを作ったとします。このライブラリの中には2つのモジュールがあります。PrimaryColor と SecondaryColor という2つのenumを含む kinds モジュールと、mix という名前の関数を含む utils モジュールです。これをリスト14-3に示します。
//! # Art
//!
//! A library for modeling artistic concepts.
pub mod kinds {
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}
/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
Orange,
Green,
Purple,
}
}
pub mod utils {
use crate::kinds::*;
/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
// --snip--
unimplemented!();
}
}
図14-3は、cargo doc によって生成されたこのクレートのドキュメントのトップページがどのように見えるかを示しています。
図14-3: kinds と utils モジュールをリストアップした art のドキュメントのトップページ
PrimaryColor と SecondaryColor 型、そして mix 関数がトップページにリストされていないことに注意してください。それらを見るには kinds と utils をクリックする必要があります。
このライブラリに依存する別のクレートは、art からアイテムをスコープに取り込むために use 文を必要とし、現在定義されているモジュール構造を指定する必要があります。リスト14-4は、art クレートから PrimaryColor と mix アイテムを使用するクレートの例を示しています。
use art::kinds::PrimaryColor;
use art::utils::mix;
fn main() {
let red = PrimaryColor::Red;
let yellow = PrimaryColor::Yellow;
mix(red, yellow);
}
art クレートを使用しているリスト14-4のコードの作者は、PrimaryColor が kinds モジュールに、mix が utils モジュールにあることを把握する必要がありました。art クレートのモジュール構造は、それを使用する人よりも、art クレートを開発している開発者にとってより意味のあるものです。内部構造は、art クレートの使い方を理解しようとしている人にとって有用な情報を含んでおらず、むしろ混乱を招きます。なぜなら、それを使う開発者はどこを探せばよいか把握する必要があり、use 文でモジュール名を指定しなければならないからです。
公開APIから内部構成を削除するために、リスト14-3の art クレートのコードを修正して、pub use 文を追加し、アイテムをトップレベルで再エクスポートすることができます。これをリスト14-5に示します。
//! # Art
//!
//! A library for modeling artistic concepts.
pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;
pub mod kinds {
// --snip--
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}
/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
Orange,
Green,
Purple,
}
}
pub mod utils {
// --snip--
use crate::kinds::*;
/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
SecondaryColor::Orange
}
}
cargo doc がこのクレートのために生成するAPIドキュメントは、図14-4に示すように、トップページに再エクスポートをリストし、リンクするようになります。これにより、PrimaryColor と SecondaryColor 型、そして mix 関数がより見つけやすくなります。
図14-4: 再エクスポートをリストした art のドキュメントのトップページ
art クレートの利用者は、リスト14-4で示したように、リスト14-3の内部構造をまだ見たり使ったりすることができます。あるいは、リスト14-6で示すように、リスト14-5のより便利な構造を使うこともできます。
use art::PrimaryColor;
use art::mix;
fn main() {
// --snip--
let red = PrimaryColor::Red;
let yellow = PrimaryColor::Yellow;
mix(red, yellow);
}
ネストされたモジュールが多い場合、pub use で型をトップレベルに再エクスポートすることは、クレートを使用する人々の体験に大きな違いをもたらすことができます。pub use のもう一つの一般的な用途は、依存関係の定義を現在のクレートで再エクスポートし、そのクレートの定義を自分のクレートの公開APIの一部にすることです。
有用な公開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.toml に保存します。このトークンは秘密の情報であることに注意してください。他の誰とも共有しないでください。もし何らかの理由で誰かと共有してしまった場合は、crates.io でそれを無効化し、新しいトークンを生成すべきです。
新しいクレートにメタデータを追加する
公開したいクレートがあるとします。公開する前に、クレートの Cargo.toml ファイルの [package] セクションにいくつかのメタデータを追加する必要があります。
あなたのクレートには一意な名前が必要です。ローカルでクレートを開発している間は、好きな名前をクレートに付けることができます。しかし、[crates.io](https://crates.io/)<!-- ignore -->上のクレート名は先願主義で割り当てられます。一度クレート名が取得されると、他の誰もその名前でクレートを公開することはできません。クレートの公開を試みる前に、使用したい名前を検索してください。もしその名前が使われていたら、別の名前を見つけ、`[package]`セクション下の_Cargo.toml_ファイルの`name`フィールドを編集して、公開用に新しい名前を使用する必要があります。以下のようになります。
<span class="filename">ファイル名: Cargo.toml</span>
```toml
[package]
name = "guessing_game"
たとえ一意な名前を選んだとしても、この時点でcargo publishを実行してクレートを公開すると、警告とそれに続くエラーが表示されます。
$ cargo publish
Updating crates.io index
warning: manifest has no description, license, license-file, documentation, homepage or repository.
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
--snip--
error: failed to publish to registry at https://crates.io
Caused by:
the remote server responded with an error (status 400 Bad Request): missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for more information on configuring these fields
これは、いくつかの重要な情報が欠落しているためにエラーになります。人々があなたのクレートが何をするものか、どのような条件で使用できるかを知るために、説明(description)とライセンス(license)が必要です。_Cargo.toml_には、検索結果であなたのクレートと共に表示されるため、1、2文程度の説明を追加してください。licenseフィールドには、_ライセンス識別子の値_を指定する必要があります。Linux Foundationの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.toml_ファイルは次のようになります。
ファイル名: Cargo.toml
[package]
name = "guessing_game"
version = "0.1.0"
edition = "2024"
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 crates.io index
Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
Packaged 6 files, 1.2KiB (895.0B compressed)
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` profile [unoptimized + debuginfo] target(s) in 0.19s
Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
Uploaded guessing_game v0.1.0 to registry `crates-io`
note: waiting for `guessing_game v0.1.0` to be available at registry
`crates-io`.
You may press ctrl-c to skip waiting; the crate should be available shortly.
Published guessing_game v0.1.0 at registry `crates-io`
おめでとうございます!これであなたはRustコミュニティとコードを共有し、誰でも簡単にあなたのクレートを自分のプロジェクトの依存関係として追加できます。
既存のクレートの新しいバージョンを公開する
クレートに変更を加えて新しいバージョンをリリースする準備ができたら、_Cargo.toml_ファイルで指定されているversionの値を変更して再公開します。行った変更の種類に基づいて、セマンティックバージョニングのルールを使用して、次のバージョン番号として適切なものを決定します。その後、cargo publishを実行して新しいバージョンをアップロードします。
Crates.ioからバージョンを非推奨にする
クレートの以前のバージョンを削除することはできませんが、将来のプロジェクトがそれらを新しい依存関係として追加するのを防ぐことはできます。これは、クレートのバージョンが何らかの理由で壊れている場合に便利です。そのような状況では、Cargoはクレートのバージョンのヤンク(yank)をサポートしています。
バージョンを_ヤンク_すると、新しいプロジェクトがそのバージョンに依存するのを防ぎつつ、それに依存する既存のすべてのプロジェクトは継続して動作できます。本質的に、ヤンクとは、_Cargo.lock_を持つすべてのプロジェクトが壊れることなく、将来生成される_Cargo.lock_ファイルはヤンクされたバージョンを使用しないことを意味します。
クレートのバージョンをヤンクするには、以前に公開したクレートのディレクトリで、cargo yankを実行し、どのバージョンをヤンクしたいかを指定します。例えば、guessing_gameという名前のクレートのバージョン1.0.1を公開し、それをヤンクしたい場合、guessing_gameのプロジェクトディレクトリで以下を実行します。
$ cargo yank --vers 1.0.1
Updating crates.io index
Yank guessing_game@1.0.1
コマンドに--undoを追加することで、yankを取り消して、プロジェクトが再びそのバージョンに依存できるようにすることもできます:
$ cargo yank --vers 1.0.1 --undo
Updating crates.io index
Unyank guessing_game@1.0.1
yankはどのコードも_削除しません_。例えば、誤ってアップロードしてしまった秘密情報を削除することはできません。もしそうなった場合は、それらの秘密情報を直ちにリセットしなければなりません。