Rust 学习笔记:发布一个 crate 到 crates.io

Rust 学习笔记:发布一个 crate 到 crates.io

我们已经使用过来自 crates.io 的包作为我们项目的依赖项,但是你也可以通过发布你自己的包与其他人共享你的代码。在 crates.io 上的 crate 注册表分发包的源代码,因此它主要托管开源代码。

Rust 和 Cargo 的特性使得发布的包更容易被人们找到和使用。接下来我们将讨论其中的一些特性,然后解释如何发布一个包。

提供有用的文档注释

之前我们讨论了如何使用两个斜杠 // 注释 Rust 代码。

Rust 还为文档提供了一种特殊的注释,方便地称为文档注释,它将生成 HTML 文档,其中显示了公共 API 项的文档注释内容。

文档注释使用三个斜杠 ///,,并支持 Markdown 符号来格式化文本。将文档注释放在他们要记录的项目的前面。

下面代码显示了一个名为 my_crate 的 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 函数的代码。我们可以通过运行 cargo doc 从这个文档注释生成 HTML 文档。该命令运行随 Rust 分发的 rustdoc 工具,并将生成的 HTML 文档放在 target/doc 目录中。

为方便起见,运行 cargo doc --open 将为当前 crate 的文档(以及所有 crate 依赖项的文档)构建 HTML,并在 web 浏览器中打开结果。

在打开的文档中导航到 add_one 函数:

在这里插入图片描述

常用标题

我们使用 # Examples Markdown 标题在 HTML 中创建了一个标题为 “Examples” 的部分。以下是 crate 作者在他们的文档中经常使用的其他 Markdown 标题:

  • Panics:被记录的功能可能出现恐 panic 的场景。如果函数的调用者不希望他们的程序出现 panic,那么他们应该确保在这些情况下不调用该函数。

  • Errors:如果函数返回一个 Result,描述可能发生的错误类型以及可能导致这些错误返回的条件可能对调用者有帮助,以便他们可以编写代码以不同的方式处理不同类型的错误。

  • Safety:如果函数调用是不安全的,应该有一节解释为什么函数是不安全的,并涵盖函数期望调用者维护的不变量。

大多数文档注释不需要所有这些部分,但这是一个很好的清单,可以提醒代码的用户感兴趣的方面。

文档注释作为测试

在文档注释中添加示例代码块可以帮助演示如何使用库,这样做还有一个额外的好处:运行 cargo test 将把文档中的代码示例作为测试运行。

如果我们使用 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,我们将看到文档测试捕捉到示例和代码彼此不同步。

注释所包含的项目

注释 //! 将为包含它的项(crate 或者模块)添加文档,而不是单个函数。我们通常在 crate 根文件(按照惯例是 src/lib.js)或模块中使用这些文档注释来作为一个整体记录 crate 或模块。

例如,为了添加描述包含 add_one 函数的 my_crate 这一 crate 用途的文档,我们添加以 //! 开头的文档注释到 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--

以 //! 注释的这些行描述了整个 crate。

当我们运行 cargo doc --open 时,这些注释将显示在 my_crate 文档的首页上,位于 crate 中的公共项目列表上方:

在这里插入图片描述

项目中的文档注释对于描述 crate 和模块尤其有用。使用它们来解释 crate 的总体目的,以帮助用户理解 crate 的组织结构。

使用 pub use 导出一个方便的公共 API

发布 crate 时,公共 API 的结构是一个主要考虑因素。如果你的 crate 有一个大的模块层次结构,使用者可能很难找到他们想要使用的部分。

结构层次太多会影响使用。用户可能还会因为必须输入 use my_crate::some_module::another_module::UsefulType; 而不是使用 use my_crate::UsefulType;

之前我们介绍了如何使用 pub 关键字将项设为公共,以及如何使用 use 关键字将项引入作用域。如果结构不方便其他人从另一个库中使用,可以使用 pub use 重新导出项,以创建与私有结构不同的公共结构。重新导出在一个位置获取公共项,并使其在另一个位置公开,就好像它是在另一个位置定义的一样。

例如,假设我们创建了一个名为 art 的库,用于建模艺术概念。在这个库中有两个模块:一个 types 模块包含两个枚举 PrimaryColor 和 SecondaryColor,一个 utils 模块包含一个名为 mix 的函数。

//! # 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--
    }
}

运行 cargo doc,生成 crate 文档:

在这里插入图片描述

注意,PrimaryColor 和 SecondaryColor 类型没有在首页上列出,mix 函数也没有。我们必须点击 kinds 和 utils 来查看它们。

另一个依赖于此库的 crate 则需要使用语句将 art 中的项目带入范围,并指定当前定义的模块结构。例如:

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

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

使用 art crate 的开发者必须弄清楚 PrimaryColor 在 kinds 模块中,mix 在 utils 模块中。然而,内部结构并不包含任何有用的信息,只会徒增麻烦。

为了从公共 API 中移除内部组织,我们可以修改 art crate 中的代码,添加 pub use 语句来重新导出顶层的项目:

//! # 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--
}

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

cargo doc 为这个 crate 生成的 API 文档现在将在首页列出和链接重新导出(re-exports)的条目,使得 PrimaryColor 和 SecondaryColor 类型以及 mix 函数更容易找到。

在这里插入图片描述

这样一来,使用者可以使用更方便的导入语句:

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

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

在有许多嵌套模块的情况下,使用 pub 在顶层重新导出类型可以对使用 crate 的人的体验产生重大影响。pub 用法的另一种常见用法是重新导出当前 crate 中依赖项的定义,使该 crate 的定义成为当前 crate 的公共 API 的一部分。

创建有用的公共 API 结构与其说是一门科学,不如说是一门艺术。选择 pub 可以让你在内部结构上更加灵活,并将内部结构与你呈现给用户的内容分离开来。

设置 crates.io 账户

在发布任何 crate 之前,您需要在 crates.io 上创建一个帐户并获得一个 API 令牌。要做到这一点,请访问 crates.io 的主页。登录后,访问 https://crates.io/me/ 的帐户设置并检索 API 密钥。然后运行 cargo login 命令并在提示时粘贴 API 密钥,如下所示:

$ cargo login
abcdefghijklmnopqrstuvwxyz012345

此命令将向 Cargo 告知你的 API 令牌,并将其本地存储在 ~/.cargo/credentials 中。请注意,这个令牌不要与其他人共享。

添加 metadata 到一个新的 crate

假设你有一个想要发布的 crate,在发布之前,您需要在 crate 的 Cargo.toml 的 [package] 部分中添加一些元数据。

crate 需要一个唯一的名称。当你在本地开发一个 crate 时,你可以给它起任何你喜欢的名字。然而,crates.io 上的 crate 名称按先到先得原则分配。在尝试发布 crate 之前,搜索想要使用的名称。如果使用了该名称,则需要找到另一个名称并编辑 Cargo.toml 文件 [package] 中的名称字段,如下所示:

[package]
name = "guessing_game"

即使选择了一个唯一的名称,当运行 cargo publish 来发布此时的 crate 时,还会报错:

$ 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

因为遗漏了一些关键信息:需要描述和许可,以便人们知道这个 crate 是做什么的,以及他们可以在什么条件下使用它。在 Cargo.toml 中添加一到两句话的描述,因为它会和你的 crate 一起出现在搜索结果中。对于 license 字段,你需要提供一个许可证标识符值。Linux 基金会列出了可以用于此值的标识符。例如,要指定你已经使用 MIT 许可证授权了你的 crate,请添加 MIT 标识符:

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

如果希望使用 Linux 基金会中没有出现的许可证,则需要将该许可证的文本放在一个文件中,将该文件包含在项目中,然后使用 license-file 来指定该文件的名称,而不是使用许可证密钥。

Rust 社区中的许多人以与 Rust 相同的方式使用 MIT OR Apache-2.0 的双重许可来许可他们的项目。

你可以为你的项目指定用 OR 分隔的多个许可证标识符,从而拥有多个许可证。

有了唯一的名称、版本、描述,以及添加的许可证,准备发布的项目的 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]

参考文档:https://doc.rust-lang.org/cargo/

该文档描述了你可以指定的其他元数据,以确保其他人可以更轻松地发现和使用你的 crate。

发布到 crates.io

现在你已经创建了一个帐户,保存了 API 令牌,为 crate 选择了一个名称,并指定了所需的元数据,现在就可以发布了!发布一个 crate 会向 crates.io 上传一个特定的版本,供他人使用。

发布是永久性的。版本永远不能被覆盖,代码也不能被删除。creates.io 的一个主要目标是充当代码的永久存档,以便依赖于 creates.io 的 crate 的所有项目的构建将继续工作。允许版本删除将使实现这一目标变得不可能。但是,可以发布的 crate 版本数量没有限制。

再次运行 cargo publish 命令,成功了:

$ cargo publish
    Updating 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` profile [unoptimized + debuginfo] target(s) in 0.19s
   Uploading guessing_game v0.1.0 (file:///projects/guessing_game)

现在你已经与 Rust 社区共享了你的代码,任何人都可以轻松地将你的 crate 添加为其项目的依赖项。

发布现有 crate 的新版本

对 crate 进行更改并准备发布新版本时,可以更改 Cargo 中指定的版本值。重新归档并重新发布。使用 Semantic Versioning 规则根据所做的更改类型来决定合适的下一个版本号。然后运行 cargo publish 来上传新版本。

使用 cargo yank 在 crates.io 上废弃 crate 特定的版本

虽然不能删除旧版本的 crate,但可以阻止任何未来的项目将它们添加为新的依赖项。

cargo yank 可以防止新项目依赖于该版本,同时允许所有依赖于该版本的现有项目继续进行。本质上,yank 意味着所有具有 Cargo.lock 的项目都不会中断,并且将来生成的任何 Cargo.lock 文件都不会使用被 yank 的版本。

运行 cargo yank,并指定版本:

$ 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 并不能删除任何代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值