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¶
流程三步:
- 账号与 token:crates.io 用 GitHub 登录,账户页取 API key,
cargo login粘贴(存到 ~/.cargo/credentials.toml,是机密)。 - 元数据:[package] 段必填 description 和 license(license 用 SPDX 标识符,如
MIT OR Apache-2.0;不在 SPDX 里的许可证用 license-file 指文件);名字先到先得,发布前先搜重名。 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. 可带走的¶
- dev 默认不优化、release 默认优化拉满;[profile.*] 可覆盖。
///文档注释 + Markdown;Panics/Errors/Safety 三张段落清单。- 文档里的代码块会被 cargo test 运行——示例即测试。
//!写在 crate 根/模块里,描述整个容器。- pub use 再导出:内部结构与公开 API 解耦。
- 发布要 description + license(SPDX);名字先到先得;发布不可逆。
- yank 拦住新依赖、不破坏旧 lock,但不删代码、救不了泄漏的密钥。
- workspace:一份 Cargo.lock、一个 target、依赖显式声明、-p 选包。
- cargo install 装二进制(~/.cargo/bin);cargo-xxx 自动变成 cargo xxx。
10. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| release profiles 与 opt-level | Chapter 14: More About Cargo and Crates.io | text/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」) |
-
出处:「Chapter 14: More About Cargo and Crates.io」第 46 段(
text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:46,搜「opt-level」)。 ↩ -
出处:「Chapter 14: More About Cargo and Crates.io」第 84 段(
text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:84,搜「documentation comment」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩↩ -
出处:「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」)。 ↩↩ -
出处:「Chapter 14: More About Cargo and Crates.io」第 492 段(
text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:492,搜「Workspaces」)。 ↩ -
出处:「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」)。 ↩ -
出处:「Chapter 14: More About Cargo and Crates.io」第 773 段(
text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:773,搜「cargo install」)。 ↩ -
出处:「Chapter 14: More About Cargo and Crates.io」第 808 段(
text/21-ch14-chapter-14-more-about-cargo-and-crates-io.txt:808,搜「Custom Commands」)。 ↩ -
出处:「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」)。 ↩