cargo doc --open 生成的依赖文档如何缓存?
解读
面试官问“如何缓存”,并不是想知道“文档存在哪个目录”,而是想确认候选人是否理解 Cargo 的构建缓存机制、rustdoc 的增量生成策略 以及 国内网络环境下如何加速依赖文档的拉取。
在国内 CI/CD、离线机房、多人协作场景下,文档重复生成耗时、 crates.io 索引拉取失败、docs.rs 无法访问都是高频痛点。能把“缓存”拆成 索引缓存 + 构建产物缓存 + 网络层缓存 三层来回答,才能体现工程化思维。
知识点
- 构建产物缓存路径
Cargo 把文档输出到target/doc
,与target/debug
/target/release
并列;同一工作空间内所有成员共享该目录,增量生成粒度为 crate-level,mtime 不变即跳过。 - CARGO_TARGET_DIR 重定向
通过环境变量CARGO_TARGET_DIR
可把target
目录指向 公司 NAS、Docker Volume 或 GitLab Cache 路径,实现跨构建复用。 - rustdoc 缓存键
rustdoc 的缓存键 = (crate 源码哈希 + rustc 版本 + RUSTDOCFLAGS)。升级 nightly 或改动#[doc(hidden)]
等属性会触发全量重建。 - 依赖文档复用
依赖文档默认随本地 crate 一起生成;若把target/doc
整体缓存,docs.rs 无法访问时也不会重新去拉取 html,直接复用本地副本。 - 国内镜像加速
在$HOME/.cargo/config.toml
里配置 source.crates-io.replace-with = 'tuna'或
ustc',可把索引与原始文档源一并镜像,避免首次生成时因网络超时回退到源码渲染。 - CI 缓存策略
GitHub Actions / Gitee Go 中把~/.cargo/registry/cache
、~/.cargo/git/db
与target/doc
三目录同时缓存,key 模板推荐cargo-doc-${{ runner.os }}-${{ rustc_hash }}-${{ hashFiles('Cargo.lock') }}
,既保证工具链升级后重建,又能在锁文件不变时命中缓存。 - 只生成依赖文档
使用cargo doc --no-deps -p <pkg>
可排除本地 crate,CI 里先跑一遍cargo doc
缓存依赖文档,再跑单元测试,节省整体流水线时间。
答案
cargo doc 的缓存分三层:
- 构建产物层:文档统一落在
target/doc
,rustdoc 以 crate 为粒度做增量;通过CARGO_TARGET_DIR
可把该目录挂载到共享存储,实现多人或 CI 复用。 - 依赖源码层:
~/.cargo/registry/cache
保存已下载的.crate
压缩包,~/.cargo/git/db
保存 git 依赖;国内用户配置 清华或中科大镜像 后,首次拉取速度可从分钟级降到秒级。 - 网络层:若依赖启用了
#[doc(html_root_url = "https://docs.rs/...")]
,浏览器会跳转到 docs.rs;在离线机房可把target/doc
整体作为静态站点,用 nginx 提供/crate-name
路由,完全摆脱外网。
最佳实践:
- 在 CI 里把
target/doc
、~/.cargo/registry/cache
与 rustc 版本、Cargo.lock 哈希一起作为缓存键; - 升级工具链后主动 bust cache,防止新旧 rustdoc 渲染差异导致隐式失效;
- 对于大型单仓库,先执行
cargo doc --workspace --no-deps
生成依赖文档并上传至 内网 S3/Artifactory,后续测试与部署阶段直接拉取,节省 30%~50% 流水线时间。
拓展思考
- 分布式缓存:如果团队规模上千人,可把
target/doc
做成 只读 FUSE 挂载点,结合sccache
的 rustdoc 支持,实现 跨机器共享已渲染 HTML。 - 版本化文档:利用
cargo-metadata
拿到精确版本号,把target/doc
重命名为target/doc/<rustc>-<timestamp>
,通过 符号链接切换,并行保留 nightly 与 stable 两套文档,方便回溯。 - 安全加固:
target/doc
里可能含有 #[doc(hidden)] 的私有 API 说明,在内网发布前可用rustdoc --cap-lints forbid
重新扫描,防止敏感接口意外暴露。