常见问题
计划是使用 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
的合并冲突问题。