Cargo.toml vs 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"

你可以看到这里有更多的信息,包括你用来构建的确切修订版本。现在,当你把你的包给别人的时候,他们将使用完全相同的 SHA,即使你没有在你的 Cargo.toml 中指定它。

当你准备选择使用库的新版本时,Cargo 可以重新计算依赖项并为你更新内容:

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

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