常见问题解答

计划使用 GitHub 作为包存储库吗?

不。Cargo 的计划是使用 crates.io,就像 npm 或 Rubygems 使用 npmjs.comrubygems.org 一样。

我们计划永远支持 git 仓库作为软件包的来源,因为它们可以用于早期开发和临时补丁,即使人们将注册表作为软件包的主要来源。

为什么构建 crates.io 而不是使用 GitHub 作为注册表?

我们认为支持多种下载软件包的方式非常重要,包括从 GitHub 下载并将软件包复制到您自己的软件包中。

也就是说,我们认为 crates.io 提供了许多重要的好处,并且很可能成为人们在 Cargo 中下载软件包的主要方式。

作为先例,Node.js 的 npm 和 Ruby 的 bundler 都支持中心注册表模型和基于 Git 的模型,并且大多数软件包都是通过这些生态系统中的注册表下载的,其中一小部分软件包使用基于 git 的软件包。

使中心注册表在其他语言中流行的优点包括

  • 可发现性。中心注册表提供了一个轻松查找现有软件包的地方。结合标签,这还使得注册表可以提供生态系统范围的信息,例如最受欢迎或最依赖的软件包列表。
  • 速度。中心注册表使得可以轻松快速有效地获取软件包的元数据,然后有效地下载已发布的软件包,而不是存储库中可能存在的其他臃肿内容。这大大提高了依赖项解析和获取的速度。随着依赖图的扩展,下载所有 git 存储库会迅速陷入困境。还要记住,并非所有人都有高速、低延迟的互联网连接。

Cargo 可以与 C 代码(或其他语言)一起使用吗?

可以!

Cargo 处理编译 Rust 代码,但我们知道许多 Rust 包会链接到 C 代码。我们也知道围绕编译 Rust 以外的语言已经构建了数十年的工具。

我们的解决方案:Cargo 允许软件包指定一个脚本(用 Rust 编写),在调用 rustc 之前运行。Rust 用于实现特定于平台的配置并重构软件包之间的常见构建功能。

可以在 make (或 ninja,或 ...) 中使用 Cargo 吗?

确实可以。虽然我们打算将 Cargo 作为一种在顶层编译 Rust 软件包的独立方式使用,但我们知道有些人希望从其他构建工具调用 Cargo。

我们已经将 Cargo 设计为在这些上下文中良好运行,注意诸如错误代码和机器可读输出模式之类的事情。我们仍然需要在这些方面做一些工作,但是从一开始就设计了在传统脚本的上下文中使用 Cargo,并且我们将继续优先考虑。

Cargo 是否处理多平台软件包或交叉编译?

Rust 本身提供了用于基于平台配置代码部分的功能。Cargo 还支持特定于平台的依赖项,并且我们计划在将来在 Cargo.toml 中支持更多每平台配置。

从长远来看,我们正在研究使用 Cargo 方便地交叉编译软件包的方法。

Cargo 是否支持诸如 productiontest 之类的环境?

我们通过使用配置文件来支持环境:

  • 特定于环境的标志(例如,开发环境使用 -g --opt-level=0,生产环境使用 --opt-level=3)。
  • 特定于环境的依赖项(例如,测试断言使用 hamcrest)。
  • 特定于环境的 #[cfg]
  • cargo test 命令

Cargo 在 Windows 上可以使用吗?

可以!

需要对 Cargo 的所有提交在 Windows 上通过本地测试套件。如果在 Windows 上运行时遇到问题,我们将其视为错误,因此请提交问题

为什么要在版本控制中包含 Cargo.lock

虽然cargo new 默认在版本控制中跟踪 Cargo.lock,但是否这样做取决于您的软件包的需求。

Cargo.lock 锁定文件的目的是描述成功构建时的世界状态。Cargo 使用锁定文件通过确保使用与最初生成 Cargo.lock 文件时完全相同的依赖项和版本,从而在不同时间和不同系统上提供确定性构建。

确定性构建有助于:

  • 运行 git bisect 以查找错误的根本原因
  • 确保 CI 仅因新的提交而失败,而不是外部因素
  • 当贡献者看到与其他人或 CI 相比不同的行为时,减少困惑

当需要针对一致版本的依赖项验证项目时,此依赖项快照也很有帮助,例如当:

  • 验证低于依赖项最新版本支持的最低支持 Rust 版本 (MSRV)
  • 验证不具有兼容性保证的人类可读输出(例如,快照测试错误消息以确保它们是“可理解的”,这是一个太模糊而无法自动化的指标)

但是,这种确定性可能会给人一种虚假的安全感,因为 Cargo.lock 不会影响您的软件包的消费者,只有 Cargo.toml 会影响。例如:

  • cargo install 将选择最新的依赖项,除非传入 --locked
  • 新的依赖项,例如通过 cargo add 添加的依赖项,将锁定到最新版本

锁定文件也可能成为合并冲突的来源。

有关通过 CI 验证较新版本依赖项的策略,请参阅验证最新依赖项

库可以使用 * 作为其依赖项的版本吗?

从 2016 年 1 月 22 日起,crates.io 拒绝所有带有通配符依赖项约束的软件包(不仅仅是库)。

严格来说,库可以这样做,但不应该这样做。版本要求为 * 表示“这将适用于所有版本”,这永远不会是真的。库应始终指定它们适用的范围,即使它像“每个 1.x.y 版本”那样笼统。

为什么是 Cargo.toml

作为与 Cargo 最频繁的交互之一,为什么配置文件命名为 Cargo.toml 的问题时不时会出现。选择首字母大写的 C 是为了确保清单与目录列表中其他类似的配置文件分组在一起。文件排序通常将大写字母放在小写字母之前,从而确保将 MakefileCargo.toml 等文件放在一起。选择后缀 .toml 是为了强调该文件采用 TOML 配置格式这一事实。

Cargo 不允许其他名称(例如 cargo.tomlCargofile)出现,以强调如何轻松识别 Cargo 存储库。历史上,多种可能的名称选择导致了混淆,其中一种情况得到了处理,但其他情况被意外遗忘了。

Cargo 如何离线工作?

--offline--frozen 标志告诉 Cargo 不要访问网络。如果它要访问网络,则会返回错误。您可以在一个项目中使用 cargo fetch 在离线之前下载依赖项,然后在另一个项目中使用相同的依赖项。请参阅配置值) 通过 Cargo 配置进行设置。

供应商化也相关,有关更多信息,请参阅有关源替换的文档。

为什么 Cargo 要重建我的代码?

Cargo 负责增量编译项目中的 crate。这意味着如果您两次键入 cargo build,则第二次不应重建您的 crates.io 依赖项。尽管如此,还是会出现错误,并且 Cargo 有时会在您不期望的时候重建代码!

我们早就希望提供更好的诊断信息,但不幸的是,很长一段时间以来我们一直无法在该问题上取得进展。但是,同时,您可以通过设置 CARGO_LOG 环境变量来至少稍微调试一下重建过程。

$ CARGO_LOG=cargo::core::compiler::fingerprint=info cargo build

这将导致 Cargo 打印出大量有关诊断和重建的信息。这通常包含有关为什么您的项目正在被重建的线索,尽管您通常需要自己连接一些点,因为此输出尚不十分易于阅读。请注意,CARGO_LOG 需要设置为您认为不应重建时执行重建的命令。不幸的是,Cargo 目前无法进行事后调试“为什么要重建?”

我们历史上看到的一些可能导致 crate 被重建的问题是:

  • 构建脚本打印 cargo::rerun-if-changed=foo,其中 foo 是一个不存在的文件,并且没有任何东西生成它。在这种情况下,Cargo 将继续运行构建脚本,认为它会生成该文件,但实际上什么也没有。解决方法是避免在这种情况下打印 rerun-if-changed

  • 两个连续的 Cargo 构建可能在某些依赖项启用的功能集上有所不同。例如,如果第一个构建命令构建整个工作区,而第二个命令仅构建一个 crate,则可能会导致 crates.io 的依赖项启用不同的功能集,从而导致它及其所依赖的所有内容都被重建。不幸的是,对于这种情况没有真正的好的解决方法,尽管如果可能,最好使 crate 上启用的功能集保持不变,而不管您在工作区中构建什么。

  • 某些文件系统在时间戳方面表现出异常行为。Cargo 主要使用文件上的时间戳来管理是否需要发生重建,但是如果您使用的是非标准文件系统,则可能会以某种方式影响时间戳(例如,截断时间戳,导致时间戳漂移等)。在这种情况下,请随时打开一个问题,我们可以看看是否可以以某种方式适应文件系统。

  • 并发构建过程正在删除工件或修改文件。有时,您可能有一个后台进程试图构建或检查您的项目。这些后台进程可能会令人惊讶地删除一些构建工件或触摸文件(或者可能只是意外地删除或触摸),这可能会导致重建看起来是虚假的!这里最好的解决方法是控制后台进程,以避免与您的工作发生冲突。

但是,如果在尝试调试问题后,您仍然遇到问题,请随时打开一个问题

“版本冲突”是什么意思,以及如何解决它?

未能为 x 选择一个可以解决此冲突的版本

您是否见过上面的错误消息?

对于 Cargo 用户来说,这是最令人恼火的错误消息之一。有几种情况可能导致版本冲突。下面我们将探讨可能的原因,并提供诊断技巧来帮助您解决问题。

  • 项目及其依赖项使用 links 来重复链接本地库。 Cargo 禁止链接两个具有相同原生库的包,因此即使在多层依赖的情况下也不允许。在这种情况下,错误消息会提示:Only one package in the dependency graph may specify the same links value,您可能需要手动检查并删除重复的 link 值。社区也制定了 相关的约定来缓解这种情况。

  • 当项目依赖不同的 crate 时,如果这些 crate 使用相同的依赖库,但使用的版本受到限制,导致无法确定正确的版本,也会导致冲突。错误消息会提示:all possible versions conflict with previously selected packages。您可能需要修改版本要求以使其保持一致。

  • 如果项目中有多个版本的依赖项,当使用 direct-minimal-versions 时,无法满足最低版本要求,这将导致冲突。您可能需要修改直接依赖项的版本要求,以满足相应的最低 SemVer 版本。

  • 如果依赖的 crate 没有您选择的功能,也会导致冲突。此时,您需要检查依赖的版本及其功能。

  • 当合并分支或 PR 时可能会发生冲突,如果存在非平凡的冲突,您可以重置所有“您的”更改,修复分支中的所有其他冲突,然后运行一些 cargo 命令(如 cargo treecargo check),这将使用您自己的本地更改重新更新 lockfile。如果您之前在您的分支中运行了一些 cargo update 命令,您可以再次运行它们。社区一直在寻求使用自定义合并工具来解决 Cargo.lockCargo.toml 的合并冲突。