编程语言研究书库

Cargo 进阶与 crates.io:从用到分享 — 第 14 章拆解

Cargo 进阶与 crates.io:从用到分享

这一章讲什么: 前 13 章都在"用"Cargo 和别人的 crate;这一章反过来—— 写文档、发布、管大项目。五块内容:构建调优、文档、公开 API 的设计、发布流程、workspace。 读完你能把一个库发布到 crates.io,并知道 yank、再导出这些"运维级"概念是干什么的。

1. Release Profiles:两套编译预设

profile 是预定义的、可定制的编译配置,dev(默认)与 release 各一套,互不影响。 核心开关 opt-level(0–3,优化程度):dev 默认 0——改十遍跑一遍的循环里,编译快比跑得快重要;release 默认 3——发布版只编一次、跑无数次,值得多花编译时间1。自定义:在 Cargo.toml 写 [profile.dev] opt-level = 1,覆盖默认但仍继承 profile 的其余默认项。

2. 文档:会当测试跑的注释

文档注释用三条斜杠 ///(对比普通注释 //),支持 Markdown,放在被文档项之前;cargo doc 生成 HTML(底层工具 rustdoc),cargo doc --open 顺便在浏览器打开。 读者是"想知道怎么用你 crate 的人",不是"想知道怎么实现的人"2。

常用段落三张:Panics(什么情况下会 panic——第 9 章说过的"契约要写进文档"的落点);Errors(返回 Result 时,什么条件导致什么错误);Safety(unsafe 函数必须说明调用方要守住哪些不变量,第 20 章)3。

文档测试(doc test)是 Rust 的招牌机制:文档注释里的 ``` 代码块会被 cargo test 编译并运行。 收益:示例永远和代码同步——"没有比带示例的文档更好的东西,也没有比跑不起来的示例更坏的东西"4。

//! 是"容器注释":文档写在它所在的那个项(crate 根、模块)头上,而不是后面跟着的项。惯例:src/lib.rs 开头用 //! 写整个 crate 的导言,会渲染在文档首页5。

3. 公开 API:pub use 解耦内外结构

art crate 的例子:内部按 kinds/utils 分模块组织得挺好,但用户得写 use art::kinds::PrimaryColor; use art::utils::mix;——你的内部结构对你是地图,对用户是迷宫。解法:根上写 pub use self::kinds::PrimaryColor; 等三行再导出。效果:内部组织原样保留,对外变成 use art::PrimaryColor;,文档首页直接列出再导出项6。

书的定性很诚实:"造一个顺手的公开 API 更像艺术而非科学";pub use 给你的就是"内部随便折腾、对外保持稳定"的解耦能力7。这也呼应了第 7 章的 pub use——那里讲机制,这里讲动机。

4. 发布到 crates.io

流程三步:

  1. 账号与 token:crates.io 用 GitHub 登录,账户页取 API key,cargo login 粘贴(存到 ~/.cargo/credentials.toml,是机密)。
  2. 元数据:[package] 段必填 description 和 license(license 用 SPDX 标识符,如 MIT OR Apache-2.0;不在 SPDX 里的许可证用 license-file 指文件);名字先到先得,发布前先搜重名。
  3. cargo publish:打包、本地验证编译、上传。

两条硬规矩:① 发布是永久的——版本不可覆盖、代码不可删除(crates.io 的定位是永久存档,保证依赖它的构建永远可复现);版本数量不限。② 出了问题的版本用 cargo yank --vers x.y.z 撤回:yank 不是删除——已有 Cargo.lock 的项目照常能用,新项目的依赖解析不再选中它;--undo 可反悔。yank 删不掉已经传上去的机密,传了密钥只能立刻作废密钥8。

5. Workspace:一组一起开发的 package

workspace 是一组共享同一个 Cargo.lock 和同一个 target 目录的 package。 顶层 Cargo.toml 不写 [package],写 [workspace] + members = ["adder", "add_one"](resolver = "3" 指定依赖解析器版本);在 workspace 目录里 cargo new 会自动把新 package 加进 members9。

三条运作规则:

  • 一个 target 目录:所有 crate 的编译产物进根目录 target,避免互相重复编译。
  • 依赖显式声明:workspace 不假设成员互赖,add_one = { path = "../add_one" } 这样写路径依赖;想用 rand 的每个 package 都得自己声明,但因为只有一份 Cargo.lock,全 workspace 锁在同一个 rand 版本上——互相兼容、不多下载。
  • 命令带 -p 选包:cargo run -p adder、cargo test -p add_one、cargo publish -p ...;不带 -p 就全 workspace 执行10。

6. cargo install 与自定义子命令

cargo install ripgrep 从 crates.io 下载并以 release 模式编译安装二进制到 ~/.cargo/bin(需在 PATH 里)。只能装带二进制目标的 crate11。

扩展机制零配置:PATH 里任何叫 cargo-something 的可执行文件,都能用 cargo something 调用,cargo --list 里也会出现——生态里 cargo-edit、cargo-watch 这类工具就是这么来的12。

7. 作者的判断与证据

  • "标准库小而稳,crate 补其余"——本章收尾的生态系统观,也是 Rust 与 Python"自带电池"路线的分野13。
  • "发布永久、yank 不删码"——机制事实,理由给了(可复现性)8。
  • "pub use 是艺术"——经验陈述7。

判断(我们的,不是书里的): 本章最有长期价值的是 doc test 和"发布永久"这两条的组合: 它们合起来构成 Rust 生态的信任基建——文档里的示例必然可运行(doc test), 且曾经发布的版本永不消失(永久存档),第三方依赖因此可以安全地搭积木。 整个 crates.io 的繁荣建立在这两个看似小的承诺上。 如果错,会错在: 永久存档也意味着永久责任(名字被占、坏版本撤不掉只能 yank); 对想要"发了再改"的初学者,这个设计是冷启动摩擦——但它是可复现性换的,账是划算的。

8. 边界与局限

  • 性能调优只有 opt-level 一个旋钮;没有 profiling 方法论。
  • 选什么许可证"超出本书范围",只给了社区惯例(MIT OR Apache-2.0)。
  • workspace 的更多结构变体"有多种,只展示一种常见的"。

9. 可带走的

  1. dev 默认不优化、release 默认优化拉满;[profile.*] 可覆盖。
  2. /// 文档注释 + Markdown;Panics/Errors/Safety 三张段落清单。
  3. 文档里的代码块会被 cargo test 运行——示例即测试。
  4. //! 写在 crate 根/模块里,描述整个容器。
  5. pub use 再导出:内部结构与公开 API 解耦。
  6. 发布要 description + license(SPDX);名字先到先得;发布不可逆。
  7. yank 拦住新依赖、不破坏旧 lock,但不删代码、救不了泄漏的密钥。
  8. workspace:一份 Cargo.lock、一个 target、依赖显式声明、-p 选包。
  9. cargo install 装二进制(~/.cargo/bin);cargo-xxx 自动变成 cargo xxx。

10. 原文地图

主题原书章原文位置
release profiles 与 opt-levelChapter 14: More About Cargo and Crates.iotext/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:46(搜「opt-level」)
文档注释与 cargo doc同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:84(搜「documentation comment」)
Panics/Errors/Safety 段落同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:122(搜「Commonly Used Sections」)
文档即测试同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:140(搜「Documentation Comments as Tests」)
//! 容器注释同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:163(搜「Contained Item Comments」)
pub use 再导出同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:197(搜「Exporting a Convenient Public API」)
发布与元数据同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:338(搜「Setting Up a Crates.io Account」) · text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:383(搜「missing or empty metadata」)
发布永久与 yank同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:431(搜「permanent」) · text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:468(搜「yanking」)
workspace同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:492(搜「Workspaces」)
cargo install 与子命令同上text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:773(搜「cargo install」) · text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:808(搜「Custom Commands」)

Footnotes

  1. 出处:「Chapter 14: More About Cargo and Crates.io」第 46 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:46,搜「opt-level」)。 ↩

  2. 出处:「Chapter 14: More About Cargo and Crates.io」第 84 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:84,搜「documentation comment」)。 ↩

  3. 出处:「Chapter 14: More About Cargo and Crates.io」第 122 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:122,搜「Commonly Used Sections」)。 ↩

  4. 出处:「Chapter 14: More About Cargo and Crates.io」第 140 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:140,搜「Documentation Comments as Tests」)。 ↩

  5. 出处:「Chapter 14: More About Cargo and Crates.io」第 163 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:163,搜「Contained Item Comments」)。 ↩

  6. 出处:「Chapter 14: More About Cargo and Crates.io」第 197 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:197,搜「Exporting a Convenient Public API」)。 ↩

  7. 出处:「Chapter 14: More About Cargo and Crates.io」第 331 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:331,搜「more an art than a science」)。 ↩ ↩2

  8. 出处:「Chapter 14: More About Cargo and Crates.io」第 431 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:431,搜「permanent」)与第 489 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:489,搜「does not delete any code」)。 ↩ ↩2

  9. 出处:「Chapter 14: More About Cargo and Crates.io」第 492 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:492,搜「Workspaces」)。 ↩

  10. 出处:「Chapter 14: More About Cargo and Crates.io」第 634 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:634,搜「one Cargo.lock」)与第 740 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:740,搜「-p flag」)。 ↩

  11. 出处:「Chapter 14: More About Cargo and Crates.io」第 773 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:773,搜「cargo install」)。 ↩

  12. 出处:「Chapter 14: More About Cargo and Crates.io」第 808 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:808,搜「Custom Commands」)。 ↩

  13. 出处:「Chapter 14: More About Cargo and Crates.io」第 818 段(text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:818,搜「standard library is small」)。 ↩

On this page