指定依赖项
你的 crate 可以依赖来自 crates.io 或其他注册表、git
仓库或本地文件系统子目录的其他库。你还可以临时覆盖依赖项的位置,例如,以便能够在你本地正在开发的依赖项中测试 bug 修复。你可以为不同的平台设置不同的依赖项,以及仅在开发期间使用的依赖项。让我们来看看如何执行这些操作。
指定来自 crates.io 的依赖项
Cargo 被配置为默认在 crates.io 上查找依赖项。在这种情况下,只需要名称和版本字符串。在 Cargo 指南中,我们指定了对 time
crate 的依赖
[dependencies]
time = "0.1.12"
版本字符串 "0.1.12"
被称为版本要求。它指定了在解析依赖项时可以从中选择的版本范围。在本例中,"0.1.12"
代表版本范围 >=0.1.12, <0.2.0
。如果更新在该范围内,则允许更新。在这种情况下,如果我们运行 cargo update time
,如果 0.1.13
是最新的 0.1.z
版本,cargo 应该会将我们更新到该版本,但不会将我们更新到 0.2.0
。
版本要求语法
默认要求
默认要求指定一个最低版本,并具有更新到 SemVer 兼容版本的能力。如果它们最左边的非零主/次/补丁组件相同,则认为版本是兼容的。这与 SemVer 不同,后者认为所有 1.0.0 之前的软件包都不兼容。
1.2.3
是默认要求的示例。
1.2.3 := >=1.2.3, <2.0.0
1.2 := >=1.2.0, <2.0.0
1 := >=1.0.0, <2.0.0
0.2.3 := >=0.2.3, <0.3.0
0.2 := >=0.2.0, <0.3.0
0.0.3 := >=0.0.3, <0.0.4
0.0 := >=0.0.0, <0.1.0
0 := >=0.0.0, <1.0.0
插入符要求
插入符要求是默认的版本要求策略。此版本策略允许 SemVer 兼容的更新。它们被指定为带有前导插入符 (^
) 的版本要求。
^1.2.3
是插入符要求的示例。
省略插入符是使用插入符要求的简化等效语法。虽然插入符要求是默认值,但建议尽可能使用简化语法。
log = "^1.2.3"
与 log = "1.2.3"
完全等效。
波浪符要求
波浪符要求指定一个最低版本,并具有一定的更新能力。如果你指定了一个主版本、次版本和补丁版本,或者只指定了一个主版本和次版本,则只允许补丁级别的更改。如果你只指定了一个主版本,则允许次版本和补丁级别的更改。
~1.2.3
是波浪符要求的示例。
~1.2.3 := >=1.2.3, <1.3.0
~1.2 := >=1.2.0, <1.3.0
~1 := >=1.0.0, <2.0.0
通配符要求
通配符要求允许使用通配符所在位置的任何版本。
*
、1.*
和 1.2.*
是通配符要求的示例。
* := >=0.0.0
1.* := >=1.0.0, <2.0.0
1.2.* := >=1.2.0, <1.3.0
注意:crates.io 不允许使用裸
*
版本。
比较要求
比较要求允许手动指定要依赖的版本范围或确切版本。
以下是一些比较要求的示例
>= 1.2.0
> 1
< 2
= 1.2.3
多个版本要求
如上面的示例所示,可以使用逗号分隔多个版本要求,例如 >= 1.2, < 1.5
。
预发布版本
除非专门要求,否则版本要求会排除预发布版本,例如 1.0.0-alpha
。例如,如果发布了包 foo
的 1.0.0-alpha
,则 foo = "1.0"
的要求将不匹配,并且会返回错误。必须指定预发布版本,例如 foo = "1.0.0-alpha"
。类似地,cargo install
将避免预发布版本,除非明确要求安装。
Cargo 允许自动使用“较新”的预发布版本。例如,如果发布了 1.0.0-beta
,则要求 foo = "1.0.0-alpha"
将允许更新到 beta
版本。请注意,这仅适用于相同的发布版本,foo = "1.0.0-alpha"
将不允许更新到 foo = "1.0.1-alpha"
或 foo = "1.0.1-beta"
。
Cargo 还会自动从预发布版本升级到与 semver 兼容的已发布版本。要求 foo = "1.0.0-alpha"
将允许更新到 foo = "1.0.0"
以及 foo = "1.2.0"
。
请注意,预发布版本可能不稳定,因此在使用它们时应谨慎。一些项目可能会选择在预发布版本之间发布重大更改。如果你的库也不是预发布版本,建议不要在库中使用预发布依赖项。在更新 Cargo.lock
时也应谨慎,并为预发布更新导致问题做好准备。
版本元数据
版本元数据,例如 1.0.0+21AF26D3
,将被忽略,不应在版本要求中使用。
建议:如有疑问,请使用默认版本要求运算符。
在极少数情况下,具有“公共依赖项”(重新导出依赖项或在其公共 API 中与之互操作)的软件包可能与多个 semver 不兼容的版本兼容(例如,仅使用一个在发布之间没有更改的简单类型,例如
Id
),它可能支持用户选择要使用的“公共依赖项”的版本。在这种情况下,像">=0.4, <2"
这样的版本要求可能会引起人们的兴趣。但是,如果软件包的用户也依赖它,那么他们很可能会遇到错误,并且需要通过cargo update
手动选择“公共依赖项”的版本,因为 Cargo 在解析依赖项版本时可能会选择不同的“公共依赖项”版本(请参阅 #10599)。避免将版本的上限约束为小于下一个 semver 不兼容版本(例如,避免
">=2.0, <2.4"
),因为依赖项树中的其他软件包可能需要更新的版本,从而导致无法解决的错误(请参阅 #9029)。考虑是否在你的Cargo.lock
中控制版本更合适。在某些情况下,这无关紧要,或者好处可能大于成本,包括
- 当没有其他人依赖你的软件包时,例如它只有
[[bin]]
- 当依赖于预发布软件包并希望避免重大更改时,那么完全指定的
"=1.2.3-alpha.3"
可能是合理的(请参阅 #2222)- 当一个库重新导出 proc-macro 但 proc-macro 生成的代码调用重新导出库时,则可能需要完全指定
=1.2.3
,以确保 proc-macro 不会比重新导出库新,并且不会生成使用当前版本中不存在的 API 部分的代码
指定来自其他注册表的依赖项
要指定来自 crates.io 以外的注册表的依赖项,请将 registry
键设置为要使用的注册表的名称
[dependencies]
some-crate = { version = "1.0", registry = "my-registry" }
其中 my-registry
是在 .cargo/config.toml
文件中配置的注册表名称。有关详细信息,请参阅注册表文档。
指定来自 git
仓库的依赖项
要依赖位于 git
仓库中的库,你需要指定的最小信息是带有 git
键的仓库位置
[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }
Cargo 在该位置获取 git
仓库,并遍历文件树以在 git
仓库内的任何位置找到请求的 crate 的 Cargo.toml
文件。例如,regex-lite
和 regex-syntax
是 rust-lang/regex
仓库的成员,可以通过仓库的根 URL (https://github.com/rust-lang/regex.git
) 引用,而不管它们在文件树中的位置如何。
regex-lite = { git = "https://github.com/rust-lang/regex.git" }
regex-syntax = { git = "https://github.com/rust-lang/regex.git" }
以上规则不适用于 path
依赖项。
提交的选择
如果我们只指定仓库 URL,如上面的示例所示,Cargo 会假定我们打算使用默认分支上的最新提交来构建我们的软件包。
你可以将 git
键与 rev
、tag
或 branch
键结合使用,以更具体地指定要使用的提交。以下是一个使用名为 next
的分支上的最新提交的示例
[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git", branch = "next" }
不是分支或标记的任何内容都属于 rev
键。这可以是提交哈希,例如 rev = "4c59b707"
,也可以是远程仓库公开的命名引用,例如 rev = "refs/pull/493/head"
。
rev
键可用的引用因托管仓库的位置而异。
GitHub 公开了对每个拉取请求的最新提交的引用,如上面的示例所示。其他 git 主机可能会在不同的命名方案下提供类似的内容。
更多 git
依赖项示例
# .git suffix can be omitted if the host accepts such URLs - both examples work the same
regex = { git = "https://github.com/rust-lang/regex" }
regex = { git = "https://github.com/rust-lang/regex.git" }
# a commit with a particular tag
regex = { git = "https://github.com/rust-lang/regex.git", tag = "1.10.3" }
# a commit by its SHA1 hash
regex = { git = "https://github.com/rust-lang/regex.git", rev = "0c0990399270277832fbb5b91a1fa118e6f63dba" }
# HEAD commit of PR 493
regex = { git = "https://github.com/rust-lang/regex.git", rev = "refs/pull/493/head" }
# INVALID EXAMPLES
# specifying the commit after # ignores the commit ID and generates a warning
regex = { git = "https://github.com/rust-lang/regex.git#4c59b70" }
# git and path cannot be used at the same time
regex = { git = "https://github.com/rust-lang/regex.git#4c59b70", path = "../regex" }
Cargo 在添加 git
依赖项时将其提交锁定在 Cargo.lock
文件中,并且仅在你运行 cargo update
命令时检查更新。
version
键的作用
version
键始终表示软件包在注册表中可用,无论是否存在 git
或 path
键。
当 Cargo 检索 git
依赖项时,version
键不影响使用的提交,但是 Cargo 会根据 version
键检查依赖项的 Cargo.toml
文件中的版本信息,如果检查失败则会引发错误。
在此示例中,Cargo 从 Git 中检索名为 next
的分支的 HEAD 提交,并检查 crate 的版本是否与 version = "1.10.3"
兼容
[dependencies]
regex = { version = "1.10.3", git = "https://github.com/rust-lang/regex.git", branch = "next" }
version
、git
和 path
键被视为用于解析依赖项的单独位置。有关详细说明,请参阅下面的多个位置部分。
注意:crates.io 不允许发布依赖于 crates.io 之外发布的软件包的代码的软件包(dev-dependencies 会被忽略)。有关
git
和path
依赖项的后备替代方案,请参阅多个位置部分。
访问私有 Git 仓库
有关私有仓库的 Git 身份验证帮助,请参阅 Git 身份验证。
指定路径依赖
随着时间的推移,我们从指南中的 hello_world
包的体积已经显著增大!它已经到了我们可能想要拆分出一个单独的 crate 供其他人使用的地步。为此,Cargo 支持路径依赖,它通常是位于一个存储库中的子 crate。让我们首先在 hello_world
包中创建一个新的 crate。
# inside of hello_world/
$ cargo new hello_utils
这将在内部创建一个新的文件夹 hello_utils
,其中 Cargo.toml
和 src
文件夹已准备好进行配置。要告诉 Cargo 这件事,请打开 hello_world/Cargo.toml
并将 hello_utils
添加到您的依赖项中。
[dependencies]
hello_utils = { path = "hello_utils" }
这告诉 Cargo 我们依赖一个名为 hello_utils
的 crate,它位于相对于它所在的 Cargo.toml
文件的 hello_utils
文件夹中。
下一次 cargo build
将自动构建 hello_utils
及其所有依赖项。
无本地路径遍历
本地路径必须指向带有依赖项的 Cargo.toml
的确切文件夹。与 git
依赖项不同,Cargo 不会遍历本地路径。例如,如果 regex-lite
和 regex-syntax
是本地克隆的 rust-lang/regex
仓库的成员,则必须通过完整路径引用它们。
# git key accepts the repo root URL and Cargo traverses the tree to find the crate
[dependencies]
regex-lite = { git = "https://github.com/rust-lang/regex.git" }
regex-syntax = { git = "https://github.com/rust-lang/regex.git" }
# path key requires the member name to be included in the local path
[dependencies]
regex-lite = { path = "../regex/regex-lite" }
regex-syntax = { path = "../regex/regex-syntax" }
已发布 crate 中的本地路径
不允许在 crates.io 上使用仅指定了路径的依赖项的 crate。
如果我们想发布我们的 hello_world
crate,我们需要将 hello_utils
的一个版本作为单独的 crate 发布到 crates.io,并在 hello_world
的依赖项行中指定其版本。
[dependencies]
hello_utils = { path = "hello_utils", version = "0.1.0" }
path
和 version
键一起使用的说明在 多个位置 部分。
注意:crates.io 不允许发布依赖于 crates.io 之外的代码的包,除了 dev-dependencies 之外。有关
git
和path
依赖项的备选方案,请参阅 多个位置 部分。
多个位置
可以同时指定注册表版本和 git
或 path
位置。git
或 path
依赖项将在本地使用(在这种情况下,将根据本地副本检查 version
),当发布到像 crates.io 这样的注册表时,它将使用注册表版本。不允许其他组合。示例
[dependencies]
# Uses `my-bitflags` when used locally, and uses
# version 1.0 from crates.io when published.
bitflags = { path = "my-bitflags", version = "1.0" }
# Uses the given git repo when used locally, and uses
# version 1.0 from crates.io when published.
smallvec = { git = "https://github.com/servo/rust-smallvec.git", version = "1.0" }
# N.B. that if a version doesn't match, Cargo will fail to compile!
当您将一个库拆分到同一工作区中的多个包时,这可能很有用。然后,您可以使用 path
依赖项来指向工作区中的本地包,以便在开发期间使用本地版本,并在发布后使用 crates.io 版本。这类似于指定一个 覆盖,但仅适用于此一个依赖项声明。
特定于平台的依赖项
特定于平台的依赖项采用相同的格式,但列在 target
部分下。通常,将使用类似 Rust 的 #[cfg]
语法来定义这些部分。
[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"
[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"
[target.'cfg(target_arch = "x86")'.dependencies]
native-i686 = { path = "native/i686" }
[target.'cfg(target_arch = "x86_64")'.dependencies]
native-x86_64 = { path = "native/x86_64" }
与 Rust 一样,此处的语法支持 not
、any
和 all
运算符来组合各种 cfg 名称/值对。
如果您想知道您的平台上可用的 cfg 目标,请从命令行运行 rustc --print=cfg
。如果您想知道另一个平台(例如 64 位 Windows)可用的 cfg
目标,请运行 rustc --print=cfg --target=x86_64-pc-windows-msvc
。
与您的 Rust 源代码不同,您不能使用 [target.'cfg(feature = "fancy-feature")'.dependencies]
来基于可选功能添加依赖项。请改用 [features]
部分
[dependencies]
foo = { version = "1.0", optional = true }
bar = { version = "1.0", optional = true }
[features]
fancy-feature = ["foo", "bar"]
cfg(debug_assertions)
、cfg(test)
和 cfg(proc_macro)
也是如此。这些值将不会按预期工作,并且将始终返回 rustc --print=cfg
返回的默认值。目前没有办法基于这些配置值添加依赖项。
除了 #[cfg]
语法之外,Cargo 还支持列出依赖项将应用到的完整目标
[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"
[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"
自定义目标规范
如果您使用的是自定义目标规范(例如 --target foo/bar.json
),请使用不带 .json
扩展名的基本文件名
[target.bar.dependencies]
winhttp = "0.4.0"
[target.my-special-i686-platform.dependencies]
openssl = "1.0.1"
native = { path = "native/i686" }
注意:自定义目标规范在稳定通道上不可用。
开发依赖项
您可以将 [dev-dependencies]
部分添加到您的 Cargo.toml
中,其格式与 [dependencies]
等效
[dev-dependencies]
tempdir = "0.3"
在编译用于构建的包时,不使用开发依赖项,但是它们用于编译测试、示例和基准。
这些依赖项不会传播到依赖此包的其他包。
您还可以在目标部分标题中使用 dev-dependencies
而不是 dependencies
来拥有特定于目标的开发依赖项。例如
[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"
注意:发布包时,只有指定了
version
的开发依赖项才会包含在已发布的 crate 中。对于大多数用例,发布时不需要开发依赖项,尽管某些用户(如 OS 打包程序)可能希望在 crate 中运行测试,因此如果可能,提供version
仍然可能是有益的。
构建依赖项
您可以使用其他基于 Cargo 的 crate 用于您的构建脚本。依赖项通过清单的 build-dependencies
部分声明
[build-dependencies]
cc = "1.0.3"
您还可以在目标部分标题中使用 build-dependencies
而不是 dependencies
来拥有特定于目标的构建依赖项。例如
[target.'cfg(unix)'.build-dependencies]
cc = "1.0.3"
在这种情况下,仅当主机平台与指定的目标匹配时才会构建依赖项。
构建脚本不能访问 dependencies
或 dev-dependencies
部分中列出的依赖项。构建依赖项同样对包本身不可用,除非也在 dependencies
部分下列出。包本身及其构建脚本是单独构建的,因此它们的依赖项不必一致。通过为独立目的使用独立的依赖项,Cargo 可以保持简单和清晰。
选择功能
如果您依赖的包提供了条件功能,您可以指定要使用的功能
[dependencies.awesome]
version = "1.3.5"
default-features = false # do not include the default features, and optionally
# cherry-pick individual features
features = ["secure-password", "civet"]
有关功能的更多信息,请参阅 功能章节。
在 Cargo.toml
中重命名依赖项
在 Cargo.toml
中编写 [dependencies]
部分时,您为依赖项编写的键通常与您在代码中导入的 crate 的名称匹配。但是,对于某些项目,您可能希望在代码中引用具有不同名称的 crate,而不管它在 crates.io 上如何发布。例如,您可能希望
- 避免在 Rust 源代码中需要
use foo as bar
。 - 依赖于 crate 的多个版本。
- 依赖于来自不同注册表的具有相同名称的 crate。
为了支持这一点,Cargo 在 [dependencies]
部分中支持一个 package
键,用于指定应该依赖哪个包
[package]
name = "mypackage"
version = "0.0.1"
[dependencies]
foo = "0.1"
bar = { git = "https://github.com/example/project.git", package = "foo" }
baz = { version = "0.1", registry = "custom", package = "foo" }
在此示例中,您的 Rust 代码中现在可以使用三个 crate
extern crate foo; // crates.io
extern crate bar; // git repository
extern crate baz; // registry `custom`
所有这三个 crate 在它们自己的 Cargo.toml
中都具有包名称 foo
,因此我们明确使用 package
键来通知 Cargo 我们想要 foo
包,即使我们本地称其为其他名称。如果未指定 package
键,则默认为正在请求的依赖项的名称。
请注意,如果您有一个可选的依赖项,例如
[dependencies]
bar = { version = "0.1", package = 'foo', optional = true }
您正在依赖来自 crates.io 的 crate foo
,但是您的 crate 具有 bar
功能而不是 foo
功能。也就是说,当重命名时,功能的名称会采用依赖项的名称,而不是包名称。
启用传递依赖项的工作方式类似,例如,我们可以将以下内容添加到上述清单中
[features]
log-debug = ['bar/log-debug'] # using 'foo/log-debug' would be an error!
从工作区继承依赖项
可以通过在工作区的 [workspace.dependencies]
表中指定依赖项来从工作区继承依赖项。之后,将其添加到带有 workspace = true
的 [dependencies]
表中。
除了 workspace
键之外,依赖项还可以包含以下键
optional
:请注意,不允许[workspace.dependencies]
表指定optional
。features
:这些是与[workspace.dependencies]
中声明的功能相加的
除了 optional
和 features
之外,继承的依赖项不能使用任何其他依赖项键(例如 version
或 default-features
)。
[dependencies]
、[dev-dependencies]
、[build-dependencies]
和 [target."...".dependencies]
部分中的依赖项支持引用依赖项的 [workspace.dependencies]
定义的能力。
[package]
name = "bar"
version = "0.2.0"
[dependencies]
regex = { workspace = true, features = ["unicode"] }
[build-dependencies]
cc.workspace = true
[dev-dependencies]
rand = { workspace = true, optional = true }