在 nightly 上减少目标目录大小的实验
摘要
Cargo 团队正在 Rust nightly 频道上进行实验,通过默认设置为 -Zembed-metadata=no 来减少目标目录大小,旨在收集关于这一潜在改进的反馈。
<p><a href="https://lobste.rs/s/omsqyf/experiment_reducing_target_directory">评论</a></p>
查看缓存全文
缓存时间: 2026/08/18 20:22
# 测试在 nightly 版本中减小目标目录大小 | Inside Rust 博客
来源:https://blog.rust-lang.org/inside-rust/2026/08/18/reducing-target-dir-size-on-nightly/
简要说明:Cargo 团队将推出一项实验,以评估一项拟议变更对用户的影响。Cargo 将在 *nightly* 频道默认启用 `\-Zembed\-metadata=no` 功能,这有助于在一定程度上减小目标目录的大小。本实验旨在收集关于该功能可行性的反馈。用户无需主动迁移到支持此功能,但可在此期间报告任何问题,并在需要时选择退出(opt\-out)(https://blog.rust-lang.org/inside-rust/2026/08/18/reducing-target-dir-size-on-nightly/#how-to-opt-out)。
## 这是什么?
Rust 编译产物占用大量磁盘空间,经常被用户列为最困扰的问题之一。在我们 2025 年的 Rust 现状调查中,这实际上是第二常见的报告问题 (https://blog.rust-lang.org/2026/03/02/2025-State-Of-Rust-Survey-results/#challenges-and-wishes-about-rust),仅次于编译速度。`target` 目录变得非常大的原因有多种,例如:
- Cargo 默认会从头编译整个 crate 图,产生大量构建产物。
- 调试信息占用大量磁盘空间。
- 增量编译产物占用大量磁盘空间。
虽然你可以禁用调试信息或增量编译来减小 `target` 目录的大小,但这当然会以牺牲编译速度和程序可调试性为代价。然而,目前 `target` 目录中有一部分数据即使并非必需,却占据了过多的空间。这就是“crate 元数据”,它可能在多个文件中重复存储。我们将在本文中重点讨论这一点。
多年来,Cargo 一直使用流水线编译 (pipelined compilation) (https://blog.rust-lang.org/2019/09/26/Rust-1.38.0/#pipelined-compilation) 来加速 crate 图的构建。在编译库 crate 时,它会指示编译器尽快生成一个 `.rmeta` 文件(包含使用该库所需的所有 crate 元数据),甚至在最终可执行代码生成之前。这使得依赖 crate 可以更早地开始编译。然而,一旦库编译完成,最终生成的 `.rlib` 文件将同时包含可执行代码和 Rust 特定的元数据。这意味着编译完成后,每个库 crate 的元数据会在磁盘上存储两次:一次在 `.rmeta` 文件中,一次在 `.rlib` 文件中。这会导致 `target` 目录大小的显著增加。
为了减少构建产物中不必要的数据重复,去年我们引入了一个不稳定的编译器标志 (https://github.com/rust-lang/rust/pull/137535) `-Zembed-metadata`,以及一个同名的相应 Cargo 标志 (https://github.com/rust-lang/cargo/pull/15378)。当使用 `-Zembed-metadata=no` 时,编译器将停止将元数据放入 `.rlib` 文件中,从而减小其文件大小。
不久,当 Cargo 和所使用的 `rustc` 都处于 `nightly` 频道时,Cargo 将开始默认使用 `-Zembed-metadata=no`。请注意,这是一项实验,该功能目前尚未计划稳定。我们希望评估这一变更在实践中的表现,并收集 nightly 用户的反馈。因此,如果你遇到任何与此次变更相关的问题,请进行报告 (https://github.com/rust-lang/cargo/issues)!
## 它有多大帮助?
我们使用最近的 nightly 编译器 (`rustc 1.100.0-nightly (67854e511 2026-08-15)`),在 `x86_64-unknown-linux-gnu` 目标上,以多种配置编译了两个 crate(`serde` 和 `cargo` 自身):
- `dev` 配置文件,分别启用/禁用增量编译,以及启用/禁用调试信息
- `release` 配置文件,禁用增量编译,禁用调试信息
我们可以在下表中看到结果。`Before` 列显示了使用之前默认设置 (`-Zembed-metadata=yes`) 时 `target` 目录的大小,而 `After` 列显示了使用 `-Zembed-metadata=no`(即将成为 nightly 频道的默认设置)后的目标磁盘大小。
| 配置文件 | 增量编译 | 调试信息 | Before [MiB] | After [MiB] | 减少比例 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `dev` | 是 | 是 | 179.70 | 171.29 | -4.7% |
| `dev` | 是 | 否 | 96.84 | 88.44 | -8.7% |
| `dev` | 否 | 是 | 81.12 | 72.68 | -10.4% |
| `dev` | 否 | 否 | 31.06 | 22.62 | -27.2% |
| `release` | 否 | 否 | 40.88 | 28.68 | -29.8% |
正如预期的那样,磁盘空间的节省在很大程度上取决于 crate 元数据本身占 `target` 目录的比例。如果没有增量产物且没有调试信息,例如使用 `release` 配置文件的默认配置时,节省是显著的,本例中接近 30%。另一方面,如果我们使用 `dev` 配置文件的默认设置(同时启用增量编译和生成调试信息),那么节省就更为有限(本例中约为 5%),因为调试信息和增量构建产物的大小远远超过了 crate 元数据的大小。
以下是编译 Cargo 自身的结果,略好于 serde。在最佳情况下,新的默认设置将 `target` 目录的大小减少了约 33%,在绝对量上接近 300 MiB!
| 配置文件 | 增量编译 | 调试信息 | Before [MiB] | After [MiB] | 减少比例 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `dev` | 是 | 是 | 3151.16 | 2910.88 | -7.6% |
| `dev` | 是 | 否 | 1476.00 | 1235.71 | -16.3% |
| `dev` | 否 | 是 | 2065.75 | 1825.45 | -11.6% |
| `dev` | 否 | 否 | 999.71 | 759.41 | -24.0% |
| `release` | 否 | 是 | 807.05 | 537.15 | -33.4% |
这并不能完全解决 `target` 目录过大的问题,但至少能提供一些帮助。
## 这会影响我吗?
在绝大多数情况下,使用 `-Zembed-metadata=no` 后,除了 `target` 目录变小外,你不应该注意到其他任何不同。然而,如果你由于某种原因手动链接 `.rlib` 文件,现在可能还需要使用 `--extern` 标志将对应的 `.rmeta` 文件传递给编译器,以便访问 crate 的元数据。如果你有类似这样的用例,请告知我们 (https://github.com/rust-lang/cargo/issues)!
如果操作不当,你可能会遇到类似以下的错误:
```
error: only metadata stub found for `rlib` dependency `foo`
please provide path to the corresponding .rmeta file with full metadata
```
请注意,即使使用 `-Zembed-metadata=no`,Cargo 目前也不会“提升” (uplift) (https://github.com/rust-lang/cargo/issues/17359) 对应的 `.rmeta` 文件。这意味着与 `.rlib` 文件不同,`.rmeta` 文件在叶库构建完成后不会作为最终的构建产物 (https://doc.rust-lang.org/cargo/reference/build-cache.html) 出现在 `target/` 目录中。如果你想定位它,必须在嵌套的构建目录中查找其位置,例如使用 Cargo 的 JSON 输出 (https://doc.rust-lang.org/cargo/reference/external-tools.html#json-messages)。
如果你使用的构建系统不是 Cargo,那么这个变更不会影响你。
## 如何选择退出(opt out)?
如果你希望选择退出,你可以向 Cargo 传递 `-Zembed-metadata=yes` 命令行参数(注意这是作为 Cargo 标志传递的,而不是在 `RUSTFLAGS` 中作为编译器标志,因为 Cargo 必须知道该标志的值),或者设置环境变量 `CARGO_UNSTABLE_EMBED_METADATA=true`。你也可以使用 `.cargo/config.toml` 文件来选择退出:
```toml
[unstable]
embed-metadata = true
```
## 未来的工作
通过在 `nightly` 频道默认启用此功能,我们希望了解它在实践中的表现,以及 Cargo 用户是否受到此变更的负面影响。在收集到关于该功能的反馈并了解到任何潜在问题后,Cargo 团队将决定如何继续推进:是直接稳定该功能、需要做出更改,还是存在需要我们重新考虑的重大问题。
因此,如果你遇到任何与这个新默认设置相关的问题,请在 Cargo 仓库中开启一个问题 (https://github.com/rust-lang/cargo/issues),以便我们了解这些问题。我们无法修复我们不知道的问题,谢谢!
如果你对该功能的进展感兴趣,可以关注其跟踪问题 (https://github.com/rust-lang/cargo/issues/15495)。
相似文章
Zig ELF 二进制文件代码高尔夫 (2025)
深入技术探讨如何缩小 Zig ELF 二进制文件的大小,从 2180K 缩减至 500 字节以下,通过去除调试信息、切换到 ReleaseSmall 以及使用 freestanding 目标。
提前发射元数据,可使 Rust 的构建/检查速度最高提升两倍
headstart 是一组对 rustc 和 cargo 的补丁,用于提前发射 crate 元数据(`.early-rmeta`),使依赖 crate 在其依赖项仍在对函数体进行类型检查时,就能开始针对接口元数据进行编译。在 13 个真实 Rust 项目中,干净的 `cargo check` 构建速度最高可提升 54%,`cargo build` 最高可提升 42%。
在 nightly 版本中启用下一代特征求解器 | Rust 博客
Rust 博客宣布在 nightly 版本中启用下一代特征求解器,这是一次重大编译器更新,替换核心类型系统组件,修复超过 200 个问题,并计划进行稳定化。
Show HN:18KB 的 ls 替代工具,基于 no_std Rust 和 Libc
一个仅 18KB 的极简 ls 命令替代工具,使用 Rust 编写,基于 no_std 和 Libc。
探索构建微型FUSE文件系统
本文将引导你使用Rust构建一个名为magicfs的最小FUSE文件系统,该系统使用metadata.json和blob文件作为后端存储,演示了名称查找、inode稳定性和内核缓存等核心文件系统概念。