指定依赖

你的crate能够依赖于其他库,其库可以来自 crates.io 、其他注册中心、git仓库、本地文件系统中的子目录。 你可以暂时覆盖某个依赖的路径,从而在本地检查和修复依赖的bug。 对于不同目标平台可以指定不同的依赖,也可以仅在开发阶段指定使用某个依赖。

指定crate.io中的依赖

Cargo默认从 crates.io 查找依赖。之前在 cargo指南 中指定了 time crate 作为依赖:

[dependencies]
time = "0.1.12"

"0.1.12" 这个字符串表示限定的版本,虽然貌似表示一个具体的 time 版本,但实际上表示一个范围,接受 SemVer 语义化兼容的更新。 具体来说,接受不改变 主版本号.次版本号.修订号第一个非零数字 的所有更新。 如果运行 cargo update -p time ,Cargo 会把 time 更新到 0.1.13 (假设 0.1.z 表示的最新版本),但不会更新到 0.2.0。 如果指定版本为 1.0 ,那么Cargo 会将其更新到 1.1 (假设 1.y 表示的最新版本),但不会更新到 2.00.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规则有所不同,SemVer认为在1.0.0之前都不兼容,而Cargo认为 0.x.y0.x.z兼容 (只要 y ≥ zx > 0)。

可以通过一些特殊操作符来调整选择兼容版本的逻辑,但在大多数情况不需要。

使用 ^ 符号

^ 符号表示默认策略,^1.2.31.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

指定来自其他注册中心的依赖

要指定非 crates.io 的依赖,首先须在 .cargo/config.toml 中设置该注册中心,参见 registries documentation 。 在依赖中,将 registry 字段设置为要使用的注册中心。

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

注意: crates.io 发布包不允许带有其他注册中心的依赖。

指定来自 git 的依赖

要指定位于 git 仓库的依赖,需要的最少信息是在 git 字段中给出该仓库的地址:

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

Cargo 会 fetch 该 git 仓库,并在仓库中查找所需crate对应的 Cargo.toml 文件(该文件不要求必须在地址根目录下,比如说指定的是工作空间某个成员crate,这时 git 字段只需给出工作空间的地址)。

因为没有指定其他信息,Cargo会假设使用该仓库主分支的最新提交。 也可以在 git 字段后加上 revtagbranch 字段,来指定想要的提交。下面是一个指定 next 分支上最新提交的例子:

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

如果想要指定的依赖版本不是某个分支或标签,那么用 rev 来指定。 rev 字段可以是 rev = "4c59b707" 这样的提交hash,也可以是 rev = "refs/pull/493/head" 这样的名称。 哪些引用是合法的取决于这个git仓库具体的管理组织。 Github 公开每个 pull requeset 最新提交的引用,其他git组织一般也提供类似的信息,只是可能有不同的命名规则。

一旦添加某个 git 依赖,Cargo会立即锁定到该依赖最新的提交。 之后即使有新的提交,Cargo也不会自动拉取。可以通过 cargo update 命令来手动拉取。

对于私有仓库的身份验证,参考 Git 身份验证

注意: crates.io 发布的包不允许带有 git 依赖 (git dev-dependencies 除外) 。关于备用方案,参考 Multiple locations

指定路径依赖

经过一段时间,指南 中的 hello_world 包内容增多了。可能需要把其中一部分拆出来。 Cargo 为此提供了指定路径依赖的功能,常见的情况是一个 git 仓库中有很多个子 crate 。 首先在 hello_world 项目里面创建一个新的 crate :

# hello_world/ 目录中
$ cargo new hello_utils

这将创建新的 hello_utils 文件夹,其中已经设置好了 Cargo.tomlsrc 文件夹。 为了让Cargo知道新crate的存在,需要在 hello_world/Cargo.toml 中将 hello_utils 添加为依赖:

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

这会告诉Cargo依赖了一个名为 hello_utils 的crate,在 hello_utils 文件夹中(相对于 Cargo.toml 的位置)。

搞定!下次执行 cargo build 会自动构建 hello_utils 和它的所有依赖,其他包也可以使用这个crate。 但 crates.io 发布的包不允许以路径指定某个依赖。 如果我们想发布 hello_world,就必须把 hello_utils 发布到 crates.io,然后在 hello_word 配置中指定依赖版本:

[dependencies]
hello_utils = { path = "hello_utils", version = "0.1.0" }

注意: crates.io 不允许发布带 path 依赖的包( path dev-dependencies 除外)。对于备用选择,参考Multiple locations

多依赖位置

可以同时指定注册中心、 gitpath 位置的依赖项。 gitpath 依赖项用于本地( version 会与本地副本进行对比), 而要发布到一个注册中心 (比如crates.io) 时,需要使用注册中心中的版本,不允许其他组合。例如:

[dependencies]
# 在本地时使用 `my-bitflags`
# 而上传后使用 crates.io 中的 1.0 版本
bitflags = { path = "my-bitflags", version = "1.0" }

# 在本地时使用给定的 git 仓库
# 而上传后使用 crates.io 中的1.0 版本
smallvec = { git = "https://github.com/servo/rust-smallvec.git", version = "1.0" }

# 如果 version 不能匹配,Cargo 会编译失败!

比如这种使用场景,当你把一个库拆分成工作空间中的多个包, 在开发阶段,可以用 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 来获取。 如果你想知道其他平台可用的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全名来指定依赖:

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

在构建包时不会使用开发依赖,而是用在编译测试、实例和性能测试。

这些依赖 不会 传播给依赖此包的那些包。

你也可以为特定目标指定开发依赖,只需要把 dependencies 换成 dev-denpendencies

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

注意: 当发布包时,只有那些以 version 指定的开发依赖才会包含在发布的包中。 大部分情况下,包发布后就不需要开发依赖了,但是也有一些使用者(比如操作系统包)希望在crate中运行一些测试,所以给开发依赖提供一个 version 也是有益处。

构建依赖

你可以在构建脚本中使用其他基于cargo的crate。在配置清单中指定 build-dependencies 来声明依赖。

[build-dependencies]
cc = "1.0.3"

你也可以为特定目标指定构建依赖,只需将 dependencies 替换为 build-dependencies 。例如:

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

这样,只有当主机平台满足指定目标要求时,才会构建相关依赖。

构建脚本无法使用 dependenciesdev-dependencies 中列出的依赖。 同样的,除非依赖放在 dependencies 中,包也无法使用构建依赖 (build-dependencies) 中的。 一个包和它的构建脚本是各自分开编译的,因此它们的依赖项不需要相同。 Cargo 不同目的使用各自的依赖,以保持简洁。

选择特性

如果你依赖的包提供了可选的特性(feature),可以选择使用:

[dependencies.awesome]
version = "1.3.5"
default-features = false # 不使用默认特性
                         # 手动选择单独的特性
features = ["secure-password", "civet"]

关于特性的更多信息,参考 features chapter

Cargo.toml 中重命名依赖

当你在 Cargo.toml 中添加 [dependencies] 时,依赖名称就是导入代码的crate名称。 但在某些项目中,可能想要用另一个名称来引用这个crate,而不是它在crates.io上的名称。比如,你想:

  • 避免在代码中使用 use foo as bar
  • 同时使用某个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" }

在这个例子中,这三个crate都在代码中可用:

extern crate foo; // crates.io
extern crate bar; // git repository
extern crate baz; // registry `custom`

这三个crate在自身的 Cargo.toml 里都名为 foo ,用 package 来可以声明想要的那个 foo 包,在本地用另一个别名。 如果没有指定 package ,则认为包的名称与依赖名称一致。

注意,如果你有一个可选依赖:

[dependencies]
bar = { version = "0.1", package = 'foo', optional = true }

你依赖的是 crates.io 上的 foo 包,但是,需要指定的是 bar 的特性,而不是 foo 的特性。 即,如果你给一个依赖改了名,需要使用新的名称,而不是原本的名称。

依赖传递也是如此。比如,可以把下面的条目加入配置清单:

[features]
log-debug = ['bar/log-debug'] # 用 'foo/log-debug' 会报错!

从工作空间中继承依赖

可以在工作空间的 [workspace.dependencies] 字段指定依赖, 然后在 crate 的 [dependencies] 中添加 workspace = true 就可以继承这个依赖。

除了 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 }