指定依赖
您的 crate 可以依赖来自 crates.io 或其他注册表的其他库、git 仓库,或您本地文件系统上的子目录。您还可以临时覆盖依赖项的位置 — 例如,为了能够测试您正在本地处理的依赖项中的错误修复。您可以针对不同的平台设置不同的依赖项,以及仅在开发期间使用的依赖项。让我们看看如何完成这些操作。
指定来自 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.z 是最新版本,Cargo 应该将我们更新到版本 0.1.13,但不会更新到 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"、"2.0.*"或~2.0),因为依赖树中的其他包可能需要更新的版本,从而导致无法解析的错误(参见 #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 键。
version 键不影响 Cargo 获取 git 依赖项时使用的提交,但 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,它位于 hello_utils 文件夹中,相对于写入此依赖项的 Cargo.toml 文件。
下一次运行 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 中的本地路径
仅使用路径指定的依赖项的 crate 不允许在 crates.io 上发布。
如果我们想发布我们的 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" }
# Note: 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" }
注意:自定义目标规范在稳定通道上不可用。
开发依赖项
您可以在 Cargo.toml 中添加一个 [dev-dependencies] 部分,其格式与 [dependencies] 等效
[dev-dependencies]
tempdir = "0.3"
在编译包进行构建时不会使用 Dev-dependencies,但用于编译测试、示例和基准测试。
这些依赖项不会传播到依赖此包的其他包。
您还可以通过在目标部分标题中使用 dev-dependencies 而不是 dependencies 来拥有特定目标的开发依赖项。例如
[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"
注意:发布包时,已发布的 crate 中只包含指定了
version的 dev-dependencies。对于大多数用例,发布时不需要 dev-dependencies,尽管一些用户(如操作系统打包人员)可能希望在 crate 内运行测试,因此,如果可能,提供version仍然有益。
构建依赖项
您可以依赖其他基于 Cargo 的 crate 用于您的构建脚本。依赖项通过 manifest 的 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 上的 foo crate,但您的 crate 拥有 bar 功能而不是 foo 功能。也就是说,在重命名时,功能的名称取决于依赖项的名称,而不是包的名称。
启用传递依赖项的工作方式类似,例如,我们可以将以下内容添加到上述 manifest 中
[features]
log-debug = ['bar/log-debug'] # using 'foo/log-debug' would be an error!
从工作空间继承依赖项
依赖项可以通过在工作空间的[workspace.dependencies] 表中指定来从工作空间继承。之后,将其添加到 [dependencies] 表中,并设置 workspace = true。
除了 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 }