指定依赖项

您的 crate 可以依赖于来自 crates.io 或其他注册表、git 存储库或本地文件系统子目录中的其他库。您还可以临时覆盖依赖项的位置 - 例如,为了能够测试您在本地处理的依赖项中的错误修复。您可以为不同的平台设置不同的依赖项,以及仅在开发期间使用的依赖项。让我们来看看如何做到这些。

指定来自 crates.io 的依赖项

默认情况下,Cargo 配置为在 crates.io 上查找依赖项。在这种情况下,只需要名称和版本字符串。在 Cargo 指南 中,我们指定了对 time crate 的依赖

[dependencies]
time = "0.1.12"

字符串 "0.1.12" 是一个版本要求。虽然它看起来像是 time crate 的特定*版本*,但它实际上指定了一个*范围*的版本,并允许 SemVer 兼容的更新。如果新版本号没有修改主版本、次版本、补丁分组中最左边的非零数字,则允许更新。在这种情况下,如果我们运行 cargo update time,如果它是最新的 0.1.z 版本,cargo 应该会将我们更新到 0.1.13 版本,但不会将我们更新到 0.2.0 版本。相反,如果我们将版本字符串指定为 1.0,如果它是最新的 1.y 版本,cargo 应该会更新到 1.1 版本,但不会更新到 2.0 版本。版本 0.0.x 不被认为与任何其他版本兼容。

以下是一些版本要求的示例,以及允许使用的版本

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.0.0 之前版本的方式不同。虽然 SemVer 表示在 1.0.0 之前没有兼容性,但 Cargo 认为 0.x.y0.x.z 兼容,其中 y ≥ zx > 0

可以使用 版本要求语法 部分中描述的特殊运算符进一步调整选择兼容版本的逻辑。

尽可能使用默认的版本要求策略,例如 log = "1.2.3",以最大限度地提高兼容性。

版本要求语法

插入符号要求

**插入符号要求**是默认的版本要求策略。此版本策略允许 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

**建议:**如有疑问,请使用默认的版本要求运算符。

在极少数情况下,具有“公共依赖项”(重新导出依赖项或在其公共 API 中与其互操作)的包可能与多个语义版本不兼容的版本兼容(例如,仅使用在版本之间没有更改的简单类型,例如 Id),在这种情况下,用户可以选择使用哪个版本的“公共依赖项”。在这种情况下,像 ">=0.4, <2" 这样的版本要求可能会很有用。*但是*,如果包的用户也依赖于它,则他们可能会遇到错误,并且需要通过 cargo update 手动选择“公共依赖项”的版本,因为 Cargo 在 解析依赖项版本 时可能会选择不同版本的“公共依赖项”(请参阅 #10599)。

避免将版本的 üst sınırı 限制为小于下一个语义版本不兼容的版本(例如,避免使用 ">=2.0, <2.4"),因为依赖树中的其他包可能需要更新的版本,从而导致无法解决的错误(请参阅 #9029)。请考虑在您的 Cargo.lock 中控制版本是否更合适。

在某些情况下,这无关紧要,或者好处可能大于成本,包括

  • 当没有其他人依赖于您的包时,例如它只有一个 [[bin]]
  • 当依赖于预发布包并希望避免重大更改时,则可能需要完全指定的 "=1.2.3-alpha.3"(请参阅 #2222
  • 当一个库重新导出一个过程宏,但该过程宏生成的代码调用了重新导出的库时,则可能需要完全指定的 =1.2.3,以确保该过程宏不比重新导出的库更新,并且不会生成使用当前版本中不存在的 API 部分的代码

指定来自其他注册表的依赖项

要指定来自 crates.io 以外的注册表的依赖项,请将 registry 键设置为要使用的注册表的名称

[dependencies]
some-crate = { version = "1.0", registry = "my-registry" }

其中 my-registry 是在 .cargo/config.toml 文件中配置的注册表名称。有关更多信息,请参阅 注册表文档

**注意**:crates.io 不允许发布依赖于在 crates.io 之外发布的代码的包。

指定来自 git 存储库的依赖项

要依赖于位于 git 存储库中的库,您需要指定的最低信息是使用 git 键指定存储库的位置

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

Cargo 在该位置获取 git 存储库,并遍历文件树以在 git 存储库内的任何位置找到请求的 crate 的 Cargo.toml 文件。例如,regex-literegex-syntaxrust-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 键与 revtagbranch 键组合使用,以更具体地说明要使用的提交。以下是如何使用名为 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 键的作用

无论是否存在 gitpath 键,version 键始终意味着该包在注册表中可用。

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" }

versiongitpath 键被视为解析依赖项的单独位置。有关详细说明,请参阅下面的多个位置部分。

**注意**:crates.io 不允许发布依赖于在 crates.io 本身之外发布的代码的包(开发依赖项 除外)。有关 gitpath 依赖项的回退替代方案,请参阅多个位置部分。

访问私有 Git 仓库

有关私有仓库的 Git 身份验证帮助,请参阅Git 身份验证

指定路径依赖项

随着时间的推移,我们在指南中的 hello_world 包的大小已经显著增加!它已经到了我们可能希望拆分出一个单独的 crate 供其他人使用的程度。为此,Cargo 支持**路径依赖项**,它们通常是位于一个仓库中的子 crate。让我们从在 hello_world 包中创建一个新的 crate 开始

# inside of hello_world/
$ cargo new hello_utils

这将在 hello_world 文件夹内创建一个新的 hello_utils 文件夹,其中包含一个可以配置的 Cargo.tomlsrc 文件夹。要将此情况告知 Cargo,请打开 hello_world/Cargo.toml 并将 hello_utils 添加到您的依赖项中

[dependencies]
hello_utils = { path = "hello_utils" }

这告诉 Cargo,我们依赖于一个名为 hello_utils 的 crate,该 crate 位于 hello_utils 文件夹中,相对于编写它的 Cargo.toml 文件。

下一个 cargo build 将自动构建 hello_utils 及其所有依赖项。

无本地路径遍历

本地路径必须指向包含依赖项的 Cargo.toml 的确切文件夹。与 git 依赖项不同,Cargo 不会遍历本地路径。例如,如果 regex-literegex-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" }

多个位置部分解释了 pathversion 键一起使用的情况。

**注意**:crates.io 不允许发布依赖于 crates.io 之外的代码的包,开发依赖项 除外。有关 gitpath 依赖项的回退替代方案,请参阅多个位置部分。

多个位置

可以同时指定注册表版本和 gitpath 位置。gitpath 依赖项将在本地使用(在这种情况下,将根据本地副本检查 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 一样,这里的语法支持 notanyall 运算符来组合各种 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 而不是 dependencies 来拥有特定于目标的开发依赖项。例如

[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"

**注意**:发布包时,只有指定了 version 的开发依赖项才会包含在已发布的 crate 中。对于大多数用例,发布时不需要开发依赖项,但某些用户(如操作系统打包程序)可能希望在 crate 中运行测试,因此如果可能,提供 version 仍然是有益的。

构建依赖项

您可以依赖于其他基于 Cargo 的 crate 来在您的构建脚本中使用。依赖项通过清单的 build-dependencies 部分声明

[build-dependencies]
cc = "1.0.3"

您还可以通过在目标部分标题中使用 build-dependencies 而不是 dependencies 来拥有特定于目标的构建依赖项。例如

[target.'cfg(unix)'.build-dependencies]
cc = "1.0.3"

在这种情况下,只有当主机平台与指定目标匹配时,才会构建依赖项。

构建脚本*无法*访问 dependenciesdev-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] 中声明的功能相加

除了 optionalfeatures 之外,继承的依赖项不能使用任何其他依赖项键(例如 versiondefault-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 }