常见问题
计划是使用 GitHub 作为包仓库吗?
不。Cargo 的计划是使用 crates.io,就像 npm 或 Rubygems 使用 npmjs.com 和 rubygems.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 来实现平台特定的配置,并重构包之间的通用构建功能。
Cargo 可以在 make(或 ninja 等)内部使用吗?
是的。虽然我们希望 Cargo 作为在顶层编译 Rust 包的独立方式很有用,但我们知道有些人会想从其他构建工具中调用 Cargo。
我们设计 Cargo 就是为了在这种上下文中良好工作,注意了诸如错误码和机器可读输出模式等方面。在这方面我们还有一些工作要做,但在传统脚本中使用 Cargo 是我们从一开始就设计的,并将继续优先考虑。
Cargo 处理多平台包或交叉编译吗?
Rust 本身提供了基于平台配置代码段的设施。Cargo 也支持平台特定依赖,未来我们计划在 Cargo.toml 中支持更多按平台的配置。
从长远来看,我们正在寻找使用 Cargo 方便地进行交叉编译的方法。
Cargo 支持环境,例如 production 或 test 吗?
我们通过使用配置文件(profiles)来支持环境,以支持
- 环境特定的标志(例如开发环境的
-g --opt-level=0和生产环境的--opt-level=3)。 - 环境特定的依赖(例如用于测试断言的
hamcrest)。 - 环境特定的
#[cfg] cargo test命令
Cargo 可以在 Windows 上工作吗?
是的!
所有提交到 Cargo 的代码都必须通过 Windows 上的本地测试套件。如果您在 Windows 上运行时遇到问题,我们将其视为 bug,因此请提交一个 issue。
为什么将 Cargo.lock 放在版本控制中?
虽然cargo new 默认会将 Cargo.lock 跟踪到版本控制中,但您是否这样做取决于您包的需求。
Cargo.lock 锁文件的目的是描述成功构建时的世界状态。Cargo 使用锁文件来提供在不同时间和不同系统上的确定性构建,通过确保使用的依赖和版本与最初生成 Cargo.lock 文件时完全相同。
确定性构建有助于
- 运行
git bisect来查找 bug 的根本原因 - 确保 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 是为了确保清单文件在目录列表中与其他类似配置文件分在一组。文件排序通常将大写字母排在小写字母之前,从而确保像 Makefile 和 Cargo.toml 这样的文件放在一起。选择末尾的 .toml 是为了强调该文件采用 TOML 配置格式。
Cargo 不允许使用其他名称,例如 cargo.toml 或 Cargofile,以强调 Cargo 仓库的易于识别性。历史上,多种可能的命名选项导致了混乱,其中一种情况得到了处理,而其他情况则被意外遗忘。
Cargo 如何离线工作?
--offline 或 --frozen 标志告诉 Cargo 不要访问网络。如果它会访问网络,则会返回错误。您可以在一个项目中使用 cargo fetch 在离线前下载依赖项,然后在另一个项目中使用相同的依赖项。请参阅配置值 net.offline) 通过 Cargo 配置进行设置。
vendoring 也相关,更多信息请参阅source replacement 文档。
为什么 Cargo 会重新构建我的代码?
Cargo 负责增量编译项目中的 crate。这意味着如果您输入两次 cargo build,第二次不应该重新构建您的 crates.io 依赖项,例如。然而,bug 会出现,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 主要使用文件上的时间戳来决定是否需要重新构建,但如果您使用的是非标准文件系统,它可能会以某种方式影响时间戳(例如截断它们、导致它们漂移等)。在这种情况下,请随意提交一个 issue,我们可以看看是否能以某种方式兼容该文件系统。
-
并发构建过程正在删除构建产物或修改文件。有时您可能有一个后台进程正在尝试构建或检查您的项目。这些后台进程可能会意外地删除一些构建产物或修改文件(或者仅仅是偶然),这可能导致重新构建看起来是错误的!这里最好的解决方法是管理后台进程以避免与您的工作冲突。
然而,如果您在尝试调试问题后仍然遇到问题,请随意提交一个 issue!
“版本冲突”是什么意思,以及如何解决?
未能为
x选择一个可以解决此冲突的版本
您是否见过上面的错误消息?
这是 Cargo 用户最烦人的错误消息之一。有几种情况可能导致版本冲突。下面我们将逐一讲解可能的原因,并提供诊断技巧来帮助您解决问题
-
项目及其依赖项使用链接来重复链接本地库。Cargo 禁止链接两个使用相同原生库的包,因此即使有多个依赖层级也不允许。在这种情况下,错误消息将提示:
Only one package in the dependency graph may specify the same links value,您可能需要手动检查并删除重复的链接值。社区也有现成的惯例来缓解此问题。 -
当项目依赖于不同的 crate 时,如果这些 crate 使用相同的依赖库,但使用的版本受到限制,导致无法确定正确的版本,也会引起冲突。错误消息将提示:
all possible versions conflict with previously selected packages。您可能需要修改版本要求以使其一致。 -
如果项目中有多个依赖版本,当使用
direct-minimal-versions时,无法满足最低版本要求,这将导致冲突。您可能需要相应地修改直接依赖项的版本要求,以满足最低 SemVer 版本。 -
如果依赖的 crate 不包含您选择的特性,也会导致冲突。此时,您需要检查依赖的版本及其特性。
-
合并分支或 PR 时可能会发生冲突,如果存在非平凡的冲突,您可以重置所有“您的”更改,修复分支中的所有其他冲突,然后运行一些 Cargo 命令(例如
cargo tree或cargo check),这应该会用您自己的本地更改重新更新锁文件。如果您之前在分支中运行过一些cargo update命令,这次可以重新运行它们。社区一直在探索使用自定义合并工具来解决Cargo.lock和Cargo.toml的合并冲突问题。