CargoRust 的构建系统和包管理器。大多数 Rustacean 们使用 Cargo 来管理他们的 Rust 项目,因为它可以为你处理很多任务,比如构建代码、下载依赖库并编译这些库。(我们把代码所需要的库叫做 依赖dependencies))。

最简单的 Rust 程序,比如我们刚刚编写的,没有任何依赖。如果使用 Cargo 来构建 “Hello, world!” 项目,将只会用到 Cargo 构建代码的那部分功能。在编写更复杂的 Rust 程序时,你将添加依赖项,如果使用 Cargo 启动项目,则添加依赖项将更容易。

由于绝大多数 Rust 项目使用 Cargo,本书接下来的部分假设你也使用 Cargo。如果使用 “安装” 部分介绍的官方安装包的话,则自带了 Cargo。如果通过其他方式安装的话,可以在终端输入如下命令检查是否安装了 Cargo

:::tips $ cargo —version

cargo 1.77.0 (3fe68eabf 2024-02-29)

:::

如果你看到了版本号,说明已安装!如果看到类似 command not found 的错误,你应该查看相应安装文档以确定如何单独安装 Cargo

使用 Cargo 创建项目

我们使用 Cargo 创建一个新项目,然后看看与上面的 “Hello, world!” 项目有什么不同。回到 projects 目录(或者你存放代码的目录)。接着,可在任何操作系统下运行以下命令:

:::tips

cd ~projects

$ cargo new hello_cargo

Created binary (application) hellow_cargo package

$ cd hello_cargo

:::

第二行命令新建了名为 hello_cargo 的目录和项目。我们将项目命名为 hello_cargo,同时 Cargo 在一个同名目录中创建项目文件。

进入 hello_cargo 目录并列出文件。将会看到 Cargo 生成了两个文件和一个目录:一个 Cargo.toml 文件,一个 src 目录,以及位于 src 目录中的 main.rs 文件。

下面来看看创建的项目结构:

  1. $ tree
  2. .
  3. ├── .git
  4. ├── .gitignore
  5. ├── Cargo.toml
  6. └── src
  7. └── main.rs

这也会在 hello_cargo 目录初始化了一个 git 仓库,以及一个 .gitignore 文件。如果在一个已经存在的 git 仓库中运行 cargo new,则这些 git 相关文件则不会生成;可以通过运行 cargo new —vcs=git 来覆盖这些行为。

:::tips 注意:Git 是一个常用的版本控制系统(version control systemVCS)。可以通过 —vcs 参数使 cargo new 切换到其它版本控制系统(VCS),或者不使用 VCS。运行 cargo new —help 参看可用的选项。

:::

使用文本编辑器打开 Catgo.toml 文件。

  1. [package]
  2. name = "hello_cargo" //项目名称
  3. version = "0.1.0" //当前版本
  4. edition = "2021" //Rust 大版本
  5. # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
  6. [dependencies] //下面是项目依赖

这个文件使用 TOML (Tom’s Obvious, Minimal Language) 格式,这是 Cargo 配置文件的格式。

第一行,[package],是一个片段(section)标题,表明下面的语句用来配置一个包。随着我们在这个文件增加更多的信息,还将增加其他片段(section)。

接下来的三行设置了 Cargo 编译程序所需的配置:项目的名称、项目的版本以及要使用的 Rust 版本。附录 E 会介绍 edition 的值。

最后一行,[dependencies],是罗列项目依赖的片段的开始。在 Rust 中,代码包被称为 crates。这个项目并不需要其他的 crate,不过在第二章的第一个项目会用到依赖,那时会用得上这个片段。

打开 src/main.rs 看看:

  1. fn main() {
  2. println!("Hello, world!");
  3. }

Cargo 生成项目的区别是 Cargo 将代码放在 src 目录,同时项目根目录包含一个 Cargo.toml 配置文件。

Cargo 期望源文件存放在 src 目录中。项目根目录只存放 README、license 信息、配置文件和其他跟代码无关的文件。使用 Cargo 帮助你保持项目干净整洁,一切井井有条。

如果没有使用 Cargo 开始项目,比如我们创建的 Hello,world! 项目,可以将其转化为一个 Cargo 项目。将代码放入 src 目录,并创建一个合适的 Cargo.toml 文件。

构建并运行 Cargo 项目

cargo build 构建

现在让我们看看通过 Cargo 构建和运行 “Hello, world!” 程序有什么不同!在 hello_cargo 目录下,输入下面的命令来构建项目:

  1. $ cargo build
  2. Compiling hello_cargo v0.1.0 (/Users/yog/projects/hello_cargo)
  3. Finished dev [unoptimized + debuginfo] target(s) in 0.81s

这个命令会创建一个可执行文件 target/debug/hello_cargo (在 Windows 上是 target\debug\hello_cargo.exe),而不是放在目前目录下。由于默认的构建方法是调试构建(debug build),Cargo 会将可执行文件放在名为 debug 的目录中。可以通过这个命令运行可执行文件:

  1. $./target/debug/hello_cargo# 或者在 Windows 下为 .\target\debug\hello_cargo.exe
  2. Hello, world!

首次运行 cargo build 时,也会使 Cargo 在项目根目录创建一个新文件:Cargo.lock。这个文件记录项目依赖的实际版本。这个项目并没有依赖,所以其内容比较少。你自己永远也不需要碰这个文件,让 Cargo 处理它就行了。

cargo run 运行

我们刚刚使用 cargo build 构建了项目,并使用 ./target/debug/hello_cargo 运行了程序,也可以使用 cargo run 在一个命令中同时编译并运行生成的可执行文件:

  1. # cargo run
  2. Finished dev [unoptimized + debuginfo] target(s) in 0.03s
  3. Running `target/debug/hello_cargo`
  4. Hello, world!

注意这一次并没有出现表明 Cargo 正在编译 hello_cargo 的输出。Cargo 发现文件并没有被改变,所以它并没有重新编译,而是直接运行了可执行文件。如果修改了源文件的话,Cargo 会在运行之前重新构建项目,并会出现像这样的输出:

  1. $ cargo run
  2. Compiling hello_cargo v0.1.0 (/Users/yog/projects/hello_cargo)
  3. Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
  4. Running `target/debug/hello_cargo`
  5. Hello, world!
cargo check 检查

当项目大了后,cargo run 和 cargo build 不可避免的会变慢,cargo check 可以用更快的方式来验证代码的正确性。

cargo check 要比 cargo build 快得多,因为它省略了生成可执行文件的步骤。如果你在编写代码时持续的进行检查,cargo check 可以让你快速了解现在的代码能不能正常通过编译!为此很多 Rustaceans 编写代码时定期运行 cargo check 确保它们可以编译。当准备好使用可执行文件时才运行 cargo build。

  1. # cargo check
  2. Checking hello_cargo v0.1.0 (/Users/yog/projects/hello_cargo)
  3. Finished dev [unoptimized + debuginfo] target(s) in 0.37s

:::tips Rust 虽然编译速度还行,但是还是不能与 Go 语言相提并论,因为 Rust 需要做很多复杂的编译优化和语言特性解析,甚至连如何优化编译速度都成了一门学问: 优化编译速度

:::

发布(release)构建

当项目准备好发布时,可以使用 cargo build --release 来优化编译项目。这会在 target/release 而不是 target/debug 下生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过这些优化也需要消耗更长的编译时间。这样就是为什么会有两种不同的配置:一种是为了开发,你需要经常快速重新构建;另一种是为用户构建最终程序,他不会经常重新构建,并且希望程序运行得越快越好。如果你在测试代码运行时间,请确保运行cargo build --release 并使用 target/release 下的可执行文件进行测试。

  • cargo run --release
  • cargo build --release
  1. # ./target/release/hello_cargo
  2. Hello, world!
Cargo.toml 和 Cargo.lock 区别

在 Rust 项目中,Cargo.tomlCargo.lock 两个文件扮演着重要但不同的角色:

  1. Cargo.toml:
  • 这是一个 Rust 项目的配置文件,用于描述项目的基本信息、依赖关系等。
  • 在这个文件中,你可以指定项目需要的外部库(依赖)及其版本。
  • 除了依赖,Cargo.toml 还包括项目名称、作者、版本等信息。
  • 通常情况下,开发者会在这个文件中指定依赖的大致版本,使用语义化版本控制(semver)来允许一定范围内的版本更新,以便能够接收到依赖的修复和非破坏性更新。
  1. Cargo.lock:
  • 当运行 Cargo 命令(如 cargo buildcargo update)时,Cargo 会生成或更新 Cargo.lock 文件。
  • 这个文件锁定了项目依赖的确切版本,确保所有人在构建项目时使用相同版本的依赖,从而使构建结果可预测。
  • 对于库项目,Cargo.lock 通常不会被提交到版本控制系统,因为库应该能够在依赖的多个版本中工作。对于二进制项目(最终的应用程序),Cargo.lock 应该被提交到版本控制系统中,以确保环境一致性。

总结来说,Cargo.toml 用于声明项目的依赖和其他配置,而 Cargo.lock 用于确保这些依赖的版本在所有环境中保持一致。这种设计旨在结合灵活性和确定性,使得项目在接受新的依赖更新时既有一定的灵活性,又能保证团队成员之间和部署环境中的一致性。