Cargo
也可以通过集成外部工具进行功能扩展,从而改善开发体验。它被设计成可最 大限度地提供可扩展性。开发人员可以创建命令行工具,Cargo
可以通过简单的 cargo binary- name
命令调用它们。在本节中,我们将介绍其中的一些工具。
子命令和 Cargo 安装
Cargo
的自定义命令属于子命令。这些命令通常来自 crates.io
、GitHub
,或者本地项目目录下的二进制文件,可以通过cargo install <binary crate name>
命令安装它们,或者在本地Cargo
项目目录下执行cargo install
命令。cargo-watch
工具就是类似的例子。
cargo-watch
cargo-watch
可以在代码发生变动后于后台自动构建项目,从而帮助你缩短修复、编译及运行代码的周期。默认情况下。它只会运行Rust
的类型检查程序(cargo check
命令),并且不会经历代码生成阶段(需要花费时间),所以能够缩短编译时间。也可以使用 -x
参数提供自定义命令来代替cargo check
命令。
我们可以通过运行cargo install cargo-watch
命令来安装cargo-watch
,然后可以在任何Cargo
项目下通过cargo watch
命令来运行它。现在,每当我们对项目进行了更改后,cargo-watch
都会在后台运行cargo-check
命令并为我们重新编译项目。在下面的代码中,我们产生了一个拼写错误并在后续对它进行了纠正,cargo-watch
随后为我们重新编译了该项目:
cargo-edit
cargo-edit
子命令用于自动将依赖项添加到你的 Cargo.toml
文件中。它可以添加所有种 类的依赖项,包括开发(dev
模式)依赖项和构建(build
模式)依赖项,还允许你添加任 何依赖项的特定版本。它可以通过运行 cargo install cargo-edit
命令进行安装。该子命令为 我们提供了 4 条命令:cargo add
、cargo rm
、cargo edit
、cargo upgrade
。
cargo-deb
这是另一款由社区开发的实用子命令,可以创建 Debian
软件包(扩展名为.deb
),以便 在 Debian Linux
上轻松地分发 Rust
可执行文件。我们可以通过运行 cargo install cargo-deb
命 令来安装它。在本章的末尾我们会使用此工具将 imgtool
命令行可执行文件打包成.deb
格式的软件包。
cargo-outdated
此命令可以显示 Cargo
项目中过时的软件依赖项。它可以通过执行 cargo install cargo-outdated
命令进行安装。安装完毕之后,你可以在项目目录下执行 cargo outdated
命令查看 过期的程序库(如果有的话)。
这些子命令能够与 Cargo
无缝协作的原因是社区开发人员使用命名约定创建这些二进 制软件包,例如 cargo-[cmd]
。当你使用 cargo install
命令安装这些二进制软件包时,Cargo
将已安装的二进制软件包暴露给环境变量$PATH
,然后你就可以通过 cargo <cmd>
的形式调 用它。这是 Cargo
扩展社区开发人员开发的子命令的一种简单、有效的方法。Cargo
还有许 多其他此类扩展。你可以在 GitHub
上找到所有由社区开发人员开发的子命令列表。
cargo install
命令还可以用于安装在 Rust
中的任何二进制软件包或可执行文件/应用程 序。默认情况下,它们是安装在/home/<user>/.cargo/bin/
目录下的。我们将使用它来安装 imgtool
应用程序——这将在本章的末尾进行构建,使得它在系统范围内可用。
使用 clippy 格式化代码
代码格式化是一种有助于保证程序库的质量,并遵循标准的编码习惯和惯例的实践。 Rust
生态系统中事实上的代码格式化工具是 clippy
。它为我们的代码检查出了一大堆问题 (撰写本书时大约有 291 个格式问题),从而确保生成高质量的 Rust
代码。在本小节中,我 们将安装 clippy
并在 libawesome
库中试用它,向其中添加一些“拙劣”的代码,然后查看 clippy
为我们提供的改进建议。在项目中使用 clippy
有多种方法,但我们将使用 cargo clippy
子命令的方法,因为这比较简单。clippy
可以对代码进行分析,因为它是一个编译器插件, 并且可以访问许多编译器的内部 API
。
要使用 clippy
,我们需要执行 rustup component add clippy
命令来安装它。如果你还 没有安装它,那么 rustup
将会为你安装。现在,为了演示 clippy
如何在我们的代码中指 出错误的格式,将在 myexponent
库中的 pow
函数内的 if 条件中添加一些拙劣的代码,如 下所示:
clippy
可发现一个常见的代码冗余样式,即检查布尔值是 true
还是 false
。或者我们可 以像 x == true
那样直接编写前面的 if
条件。clippy
还提供更多的代码检查,其中一些甚至 指出了代码中的潜在错误。
Cargo.toml 清单文件简介
为了获取项目的各种信息,Cargo
在很大程度上依赖于项目的清单文件 Cargo.toml
。让 我们仔细查看一下该文件的结构以及它能够包含的元素。如前所述,Cargo
新建了一个几 乎空白的清单文件,只填充了必要的字段,以便可以构建项目。每个清单文件都分为几个 部分,用于指定项目的不同属性。我们将介绍通常在中型 Cargo
项目清单文件中会用到的 属性。以下是虚构的某个大型应用程序的 Cargo.toml
文件:
#在清单文件中,我们可以使用#编写注释
[package]
name = "cargo-metadata-example"
version = "1.2.3"
description = "An example of Cargo metadata"
documentation = "https://docs.rs/dummy_crate"
license = "MIT"
readme = "README.md"
keywords = ["example", "cargo", "mastering"]
authors = ["Jack Daniels <jack@danie.ls>", "Iddie Ezzard <iddie@ezzy>"]
build = "build.rs"
edition = "2018"
[package.metadata.settings]
default-data-path = "/var/lib/example"
[features]
default=["mysql"]
[build-dependencies]
syntex = "^0.58"
[dependencies]
serde = "1.0"
serde_json = "1.0"
time = { git = "https://github.com/rust-lang/time", branch = "master" }
mysql = { version = "1.2", optional = true }
sqlite = { version = "2.5", optional = true }
让我们从[package]
开始,来看看尚未解释的部分。
description
:它包含一个关于项目的、更长的、格式自由的文本字段。license
:它包含软件许可证标识符。readme
:它允许你提供一个指向项目版本库某个文件的链接。这通常是项目简介的 入口点。documentation
:如果这是一个程序库,那么其中包含指向程序库说明文档的链接。keywords
:它是一组单词列表,有助于用户通过搜索引擎或者crates.io
网站发现你的项目。authors
:它列出了该项目的主要作者。build
:它定义了一段Rust
代码(通常是build.rs
),它在编译其余程序之前编译并 运行。这通常用于生成代码或者构建项目程序所依赖的原生库。edition
:它主要用于指定编译项目时使用的Rust
版本。在我们的示例中,使用的是2018
版本。之前的是2015
版本,如果不存在版本密钥,则默认使用此版本。注意:2018
版本创建的项目是向后兼容的,这意味着它也可以使用2015
版本的程序库作 为依赖项。
接下来是[package.metadata.settings]
。通常,Cargo
会对它无法识别的键或属性向用户发出警告,但是包含元数据的部分是个例外。它们会被 Cargo
忽略,因此可以用于配置项目所需的任何键/值对。
[features]
、[dependencies]
及[build-dependencies]
会组合到一起使用。依赖关系可以通过版本号声明,如 semver
指南中所述:
serde = "1.0"
这意味着 serde
是一个强制依赖,我们希望使用最新的版本,即“1.0.*
”。实际的版本 将会在 Cargo.lock
文件中确定。
使用补注符号(^)
可以扩展 Cargo
允许查找的版本范围:
syntex = "^0.58"
这里,我们的意图是查找最新的主版本号“0.*.*
”,并且版本号至少是“0.58.*
”或 以上。
Cargo
还允许你直接指定依赖关系到 Git
版本库,前提是版本库是由 Cargo
创建的项目, 并遵循 Cargo
期望的目录结构。我们可以像这样从 GitHub
指定依赖关系:
time = { git = "[https://github.com/rust-lang/time",](https://github.com/rust-lang/time",) branch = "master" }
这也适用于其他在线 Git
版本库,例如 GitLab
。同样,运行 cargo update
命令将在 Cargo.lock
中确定实际的调用版本(在使用 Git
版本库的情况下,此操作是指变更集的修 订版)。
清单列表还有两个可以选的依赖项,mysql
和sqlite
:
mysql = { version = "1.2", optional = true }
sqlite = { version = "2.5", optional = true }
这意味着可以在不依赖任何一个依赖项的情况下构建程序。`[features]`属性部分包含默 认功能列表:
default = ["mysql"]
这意味着用户在构建程序时如果没有手动覆盖功能集,则只会引入 mysql
而不包括 sqlite
。该特性的一个应用场景是你的程序库需要进行某种特定的优化改进。不过这在嵌入式平台上的开销会非常高,因此程序库作者只能将它作为功能进行发布,这些功能只能在能够承载它们的系统上使用。另一个应用场景是在构造命令行应用程序时,提供 GUI
前端 作为额外的特性。
这是一个关于如何使用 Cargo.toml
清单文件描述 Cargo
项目的简要介绍。有关如何使
用 Cargo
配置项目的内容还有很多。
:::info
[package]
部分
name
: 项目的名称,这里是cargo-metadata-example
。version
: 项目的版本号,遵循 语义化版本控制](https://semver.org/)),这里是1.2.3
。description
: 项目的简短描述。documentation
: 指向项目文档的 URL。这里使用[https://docs.rs/dummy_crate
](https://docs.rs/dummy_crate`) 作为示例。license
: 项目的许可证,这里是MIT
许可证。readme
: 项目的 README 文件,通常是 Markdown 格式,这里是README.md
。keywords
: 一个关键词数组,有助于在 crates.io 上搜索此项目。authors
: 项目作者的数组,每个作者的格式通常是姓名 <邮箱地址>
。build
: 指定一个构建脚本的路径,这里是build.rs
。构建脚本在编译之前运行,可用于自动生成代码等任务。edition
: 指定 Rust 的版本,这里使用的是 2018 版。
[package.metadata.settings]
部分
default-data-path
: 项目特定的配置选项,这里指定默认数据路径为/var/lib/example
。metadata
部分用于扩展 Cargo.toml,允许你添加不直接影响构建过程的自定义配置。
[features]
部分
default
: 定义了默认启用的功能,这里默认启用了mysql
功能。
[build-dependencies]
部分
- 定义了构建此项目时需要的依赖。这里只有一个构建依赖
syntex
,版本约束为^0.58
。
[dependencies]
部分
- 定义了项目运行时的依赖。
serde
和serde_json
: 序列化和反序列化库,版本都指定为1.0
。time
: 直接从 git 仓库[https://github.com/rust-lang/time
](https://github.com/rust-lang/time`) 的master
分支获取的依赖项。mysql
和sqlite
: 数据库依赖,均标记为optional
,意味着它们不会默认被包含,但可以通过功能标志启用。
:::