Cargo 也可以通过集成外部工具进行功能扩展,从而改善开发体验。它被设计成可最 大限度地提供可扩展性。开发人员可以创建命令行工具,Cargo 可以通过简单的 cargo binary- name 命令调用它们。在本节中,我们将介绍其中的一些工具。


子命令和 Cargo 安装

Cargo的自定义命令属于子命令。这些命令通常来自 crates.ioGitHub,或者本地项目目录下的二进制文件,可以通过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 工具扩展 - 图1

cargo-edit

cargo-edit 子命令用于自动将依赖项添加到你的 Cargo.toml 文件中。它可以添加所有种 类的依赖项,包括开发(dev 模式)依赖项和构建(build 模式)依赖项,还允许你添加任 何依赖项的特定版本。它可以通过运行 cargo install cargo-edit 命令进行安装。该子命令为 我们提供了 4 条命令:cargo addcargo rmcargo editcargo 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 条件中添加一些拙劣的代码,如 下所示:

Cargo 工具扩展 - 图2

clippy 可发现一个常见的代码冗余样式,即检查布尔值是 true 还是 false。或者我们可 以像 x == true 那样直接编写前面的 if 条件。clippy 还提供更多的代码检查,其中一些甚至 指出了代码中的潜在错误。

Cargo.toml 清单文件简介

为了获取项目的各种信息,Cargo 在很大程度上依赖于项目的清单文件 Cargo.toml。让 我们仔细查看一下该文件的结构以及它能够包含的元素。如前所述,Cargo 新建了一个几 乎空白的清单文件,只填充了必要的字段,以便可以构建项目。每个清单文件都分为几个 部分,用于指定项目的不同属性。我们将介绍通常在中型 Cargo 项目清单文件中会用到的 属性。以下是虚构的某个大型应用程序的 Cargo.toml 文件:

  1. #在清单文件中,我们可以使用#编写注释
  2. [package]
  3. name = "cargo-metadata-example"
  4. version = "1.2.3"
  5. description = "An example of Cargo metadata"
  6. documentation = "https://docs.rs/dummy_crate"
  7. license = "MIT"
  8. readme = "README.md"
  9. keywords = ["example", "cargo", "mastering"]
  10. authors = ["Jack Daniels <jack@danie.ls>", "Iddie Ezzard <iddie@ezzy>"]
  11. build = "build.rs"
  12. edition = "2018"
  13. [package.metadata.settings]
  14. default-data-path = "/var/lib/example"
  15. [features]
  16. default=["mysql"]
  17. [build-dependencies]
  18. syntex = "^0.58"
  19. [dependencies]
  20. serde = "1.0"
  21. serde_json = "1.0"
  22. time = { git = "https://github.com/rust-lang/time", branch = "master" }
  23. mysql = { version = "1.2", optional = true }
  24. 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 版本库的情况下,此操作是指变更集的修 订版)。


清单列表还有两个可以选的依赖项,mysqlsqlite

  1. mysql = { version = "1.2", optional = true }
  2. sqlite = { version = "2.5", optional = true }
  1. 这意味着可以在不依赖任何一个依赖项的情况下构建程序。`[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/examplemetadata 部分用于扩展 Cargo.toml,允许你添加不直接影响构建过程的自定义配置。

[features] 部分

  • default: 定义了默认启用的功能,这里默认启用了 mysql 功能。

[build-dependencies] 部分

  • 定义了构建此项目时需要的依赖。这里只有一个构建依赖 syntex,版本约束为 ^0.58

[dependencies] 部分

  • 定义了项目运行时的依赖。
  • serdeserde_json: 序列化和反序列化库,版本都指定为 1.0
  • time: 直接从 git 仓库 [https://github.com/rust-lang/time](https://github.com/rust-lang/time`) 的 master 分支获取的依赖项。
  • mysqlsqlite: 数据库依赖,均标记为 optional,意味着它们不会默认被包含,但可以通过功能标志启用。

:::