Rust 从 0 到 1-Cargo- 发布到 Crates.io
我们曾在项目中使用 crates.io 上的 package 作为依赖,反过来我们也可以通过它发布自己的 package 来将代码分享给其他人。在 crates.io 注册的 crate 会包含源代码的发布,因此它主要用于开源代码。
Rust 和 Cargo 包含了一些功能,可以帮助他人找到和开始使用你发布的 package。下面我们将对其中一些功能做介绍,然后讲如何发布一个 package 。
文档注释
准确的文档对于其他用户使用我们的 package 非常有帮助,因此花一些时间编写文档是值得的。之前我们介绍过如何使用 // 注释代码。Rust 还有一种被称为文档注释(documentation comments)的特定注释类型,它会生成 HTML 文档,用于展示公有 API 的文档注释,主要目的是帮助对如何使用我们的 crate 感兴趣的程序员,并不是对具体实现感兴趣的程序员(关心具体实现的话,阅读源码应该是更好的方式)。
文档注释使用 /// ,并支持 Markdown 语法,位置一般位于需要注释的 API 之前。参考下面的例子:
上面的例子中,我们在文档注释中对 add_one 函数的功能做了描述,接着在 Examples 的部分,展示了如何使用它。我们可以运行 cargo doc 来生成 HTML 文档并存放在 target/doc 目录下。
更方便的是,我们可以直接通过运行 cargo doc --open 生成 HTML 文档(同时还包括其所有依赖项的文档)并在浏览器中打开。导航到 add_one 函数,我们可以看到最终生成的文档类似下图所示(来自官网):
常用文档章节
上面的例子中使用了 # Examples (Markdown 语法)在 HTML 中创建了一个 “Examples” 章节。另外一些经常在文档注释中使用的章节还有:
Panics:函数可能会发生 panic! 的场景。函数调用者如果不希望程序发生 panic 需要注意避免在相应的场景调用函数。
Errors:当函数返回 Result 时,描述可能会出现的错误以及场景,帮助调用者针对性的处理错误。
Safety:如果这个函数的是 unsafe 的(后面章节会讨论 unsafe),这部分章节会解释 unsafe 的原因,并包含函数期望调用者所需要确保的不变因素(invariants,我理解为函数所假设的一些条件、约束必须为真)。
大部分文档注释并不需要所有这些章节,不过这可以作为一个检查列表,用于提醒我们调用者可能感兴趣了解的内容。
在文档注释中包含测试
在文档注释中增加示例代码除了可以清楚的说明库的使用方法以外,还有一个额外的好处:cargo test 也会运行文档中的示例代码!没有什么比有例子的文档更好了。但也没有什么比由于代码发生了修改,例子不能正常运行更糟了。在文档注释中增加示例代码可以帮助我们保持文档和代码的同步。执行 cargo test 运行前面的例子,我们可以看到类似下面的结果:
我们可以尝试改变函数或例子来看看会发生什么。;)
为注释所在项提供文档
还有另一种文档注释 //!,它不是注释之后的内容,而是为注释所在的项提供文档,通常用于 crate 根文件(通常是 src/lib.rs)或模块,为 crate 或模块整体提供说明文档。参考下面的例子:
上面的例子是包含 add_one 函数的 crate 的文档注释(my_crate)。注意 //! 最后一行之后没有任何代码。因为使用 //! 的注释属于包含此注释的项而不是注释之后的代码。在上面的例子中,包含 //! 注释的项是 src/lib.rs ,即 crate 的根文件,这些注释是对整个 crate 的描述说明。运行 cargo doc --open,我们可以看到这些注释显示在 my_crate 文档的首页,类似下图所示(来自官网):
这类注释对于描述 crate 和模块特别有用,可以通过总体介绍来帮助使用者理解 crate 的组织结构。
使用 pub use 导出公有 API
前面的章节我们介绍过如何使用 mod 来组织代码,如何使用 pub 将访问权限变为公有的,和如何使用 use 将路径引入作用域。然而,对于我们开发 crate 来说良好的组织结构可能对于外部调用者并不方便。我们可能会使用多层级的结构对代码进行组织,这样定义于较深层级中的类型或函数等将不容易被找到;同时,使用 use my_crate::some_module::another_module::UsefulType; 相对 use my_crate::UsefulType; 来说也更加繁琐。
API 的结构是我们发布 crate 时需要考虑的主要内容之一。对于使用者来说,他们并不会那么熟悉 crate 的结构,如果模块层级过深,可能会难以找到所需的内容。
好消息是,我们无需为此重新进行代码的内部组织,我们可以选择使用 pub use 进行重导出(re-export,前面介绍代码组织的时候有提到过),从而获得另外一种公开的组织结构。重导出将位于某个代码层级的公有项在另一个层级再次公开,就像它本身就定义在那里一样。下面我们通过一个例子来体验一下,首先是来看看最初的定义:
cargo doc 所生成的文档首页类似下图(来自官网):
我们可以看到 PrimaryColor 和 SecondaryColor 类型、以及 mix 函数都未在首页中列出。我们需要点击 kinds 或 utils 在下一层才能看到。如果我们需要使用 use 导入 art 中的公开类型或函数,需要包含其定义的模块结构,参看下面的例子:
如果要使用 art crate 我们就需要知道 PrimaryColor 在 kinds 模块中, mix 在 utils 模块中。art crate 的模块结构是从开发者的角度来组织的而不是使用者的角度。其用于内部组织的 kinds 模块和 utils 模块对想知道如何使用它的人没有提供任何有价值的信息。反而,需要弄清去哪查看所需的功能以及在使用时指定模块名,从而造成使用上的不便。为了消除这些影响,我们在结构的顶层使用 pub use 语句进行重导出,参考下面的例子:
再次运行 cargo doc 生成 API 文档,我们会在首页看到重导出的项以及其链接,更易于查找,类似下图所示(来自官网):
现在我们可以在代码中更方便的使用 art crate 了(当然,我们仍然可以选择向之前那样使用):
对于有多层嵌套模块的情况,使用 pub use 将进行重导出,对于使用 crate 的人来说将带来完全不同的体验。
创建易用的 API 结构更像是一门艺术而非科学,我们可以通过不断的迭代找出对使用者来说最友好的 API。pub use 让 crate 内部结构和呈现给使用者的 API 解耦。
创建 Crates.io 账号
在我们发布 crate 之前,需要在 crates.io 上注册账号并获取 API token。为此,我们需要访问 crates.io 的首页并使用 GitHub 账号登陆(目前只能使用 GitHub 账号登录,将来可能会支持其他创建账号的方式)。登陆之后,我们可以在账户设置页面( https://crates.io/me/ )获取 API token,我们可以使用 API token 运行 cargo login 命令:
这个命令会告诉 Cargo 你的 API token 并将其储存在本地的 ~/.cargo/credentials 文件中。需要注意,API token 是私密的(secret),不要分享给任何人。不管什么原因别人如果得到了你的 API token,应该立即到 crates.io 吊销并重新生成它。
为新 crate 添加元数据
现在我们有了账号,假设我们现在希望发布一个 crate。在发布之前,我们需要 Cargo.toml 文件的 [package] 部分增加 crate 的元数据(metadata)。
首先我们需要一个唯一的 crate 名称。虽然在本地开发时,我们可以使用任何喜欢的名称,但是 crates.io 上的 crate 名称遵循先到先得的原则。一旦某个 crate 名称已经被使用,其他人就不能再发布这个名称的 crate。我们可以在 crates.io 上搜索我们希望使用的名称来确认它是否已被使用。如果没有,我们就可以修改 Cargo.toml 为我们的 crate 命名:
即使 crate 的名字是唯一的,但此时运行 cargo publish 进行发布的话,我们会得到类似下面的警告和错误:
这是因为我们还缺少一些关键信息:关于 crate 的描述说明和许可协议(license)。为此,我们需要在 Cargo.toml 中加入这些信息。描述通常是一两句话,它会和 crate 一起出现在搜索结果中。对于许可协议,我们可以在 Linux Foundation’s Software Package Data Exchange (SPDX) 页面中进行选择。譬如,我们选择 MIT:
如果我们希望使用不存在于 SPDX 中的许可协议,则需要将许可协议文本放入一个文件,并包含进项目中,然后使用 license-file 来指定文件名。
关于应该使用哪种许可协议已经超出了我们讨论的范畴。很多 Rust 社区成员选择使用与 Rust 相同的许可协议,即双重许可(允许用户选择使用哪一个) MIT OR Apache-2.0(注意,我们使用关键字 OR 分隔多个许可协议)。
一个包含名称、版本号、在 cargo new 时输入的作者信息、描述和许可协议的 Cargo.toml 文件看起来类似下面这样:
更多的元信息项我们可以查阅官方的 Cargo 文档 ,它们可以让 crate 更容易被发现和使用!
发布到 Crates.io
现在我们有了账号,保存了 API token,并指定了所需的元数据,已经准备好发布我们的 crate 了!发布 crate 要小心,因为发布是永久性的(permanent)。相同的版本无法覆盖,代码也无法删除。crates.io 一个主要目标就是作为存储代码的永久服务器,这样所有依赖 crates.io 中的 crate 的项目都可以一直正常工作。如果允许进行删除,则可能造成依赖的 crate 无法工作。但是,我们可以发布的版本数量是没有限制的。
再次运行 cargo publish 命令,应该会成功:
我们现在向 Rust 社区分享了代码,而且任何人都可以轻松的将你的 crate 加入项目的依赖。
发布新版本
当我们修改了 crate 代码,并准备好再次发布时,可以通过修改 Cargo.toml 中 version 的值指定新的版本号。官方建议根据 Semantic Versioning rules 来指定版本(MAJOR.MINOR.PATCH,具体规则不在此说明了,大家可以自行搜索) 。最后运行 cargo publish 来就可以发布新的版本了。
“删除”版本
虽然我们不能删除已经发布的版本,但是可以阻止之后的项目将它们作为依赖。这在某个版本因为这样或那样的原因不建议使用的场景很有用。对于这种情况,Cargo 通过 yank 来解决。
yank 某个版本会阻止之后的项目依赖此版本,不过所有已经依赖的项目仍然能够正常使用。本质上说,yank 意味着所有已经包含 Cargo.lock 的项目不会被破坏,而任何新生成 Cargo.lock 的项目将不能使用被 yank 的版本。
我们可以通过运行 cargo yank 来“删除”指定的版本:
我们还可以通过在命令上增加 --undo 参数撤销 yank 操作,允许项目可以依赖某个曾经被 yank 的版本:
yank 并没有删除任何代码。因此,假如你不小心上传了你的密码,请立即进行修改或重置。
版权声明: 本文为 InfoQ 作者【山】的原创文章。
原文链接:【http://xie.infoq.cn/article/46b14135befb7a76d7a83d582】。文章转载请联系作者。
评论