Cargo.toml 与 Cargo.lock

Cargo.tomlCargo.lock 用于两个不同的目的。在我们讨论它们之前,这里有一个总结

  • Cargo.toml 用于以广义的方式描述您的依赖项,由您编写。
  • Cargo.lock 包含有关您的依赖项的确切信息。它由 Cargo 维护,不应手动编辑。

如有疑问,请将 Cargo.lock 提交到版本控制系统(例如 Git)。有关原因和替代方案的更多信息,请参阅常见问题解答中的“为什么要在版本控制中使用 Cargo.lock?”。我们建议将其与验证最新依赖项配对使用

让我们更深入地研究一下。

Cargo.toml 是一个清单文件,我们可以在其中指定有关包的大量不同元数据。例如,我们可以说我们依赖于另一个包

[package]
name = "hello_world"
version = "0.1.0"

[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }

此包对 regex 库有一个依赖项。在这种情况下,我们声明我们依赖于 GitHub 上的特定 Git 存储库。由于我们没有指定任何其他信息,因此 Cargo 假定我们打算使用默认分支上的最新提交来构建我们的包。

听起来不错?好吧,有一个问题:如果您今天构建此包,然后将其发送给我,而我明天构建此包,则可能会发生不好的事情。在此期间,regex 可能会有更多提交,我的构建将包含新提交,而您的构建则不会。因此,我们将获得不同的构建。这很糟糕,因为我们希望构建是可重现的。

我们可以通过在 Cargo.toml 中定义特定的 rev 值来解决此问题,以便 Cargo 在构建包时确切知道要使用哪个版本

[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git", rev = "9f9f693" }

现在我们的构建将是相同的。但是有一个很大的缺点:现在我们每次想要更新库时都必须手动考虑 SHA-1。这既乏味又容易出错。

输入 Cargo.lock。由于它的存在,我们不需要手动跟踪确切的版本:Cargo 会为我们做这件事。当我们有这样的清单时

[package]
name = "hello_world"
version = "0.1.0"

[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }

Cargo 将获取最新的提交,并在我们第一次构建时将该信息写入我们的 Cargo.lock。该文件将如下所示

[[package]]
name = "hello_world"
version = "0.1.0"
dependencies = [
 "regex 1.5.0 (git+https://github.com/rust-lang/regex.git#9f9f693768c584971a4d53bc3c586c33ed3a6831)",
]

[[package]]
name = "regex"
version = "1.5.0"
source = "git+https://github.com/rust-lang/regex.git#9f9f693768c584971a4d53bc3c586c33ed3a6831"

您可以看到这里有更多信息,包括我们用来构建的确切版本。现在,当您将包交给其他人时,即使我们没有在 Cargo.toml 中指定,他们也会使用完全相同的 SHA。

当我们准备好选择新版本的库时,Cargo 可以重新计算依赖项并为我们更新

$ cargo update         # updates all dependencies
$ cargo update regex   # updates just “regex”

这将写出一个新的 Cargo.lock,其中包含新的版本信息。请注意,cargo update 的参数实际上是一个包 ID 规范,而 regex 只是一个简短的规范。