Rust 包管理器 Cargo 入门

了解 Rust 的包管理器和构建工具。
86 位读者喜欢这篇文章。
Shipping containers stacked in a yard

Lucarelli via Wikimedia Commons。CC-BY-SA 3.0

Rust 是一种现代编程语言,它提供性能、可靠性和生产力。在 StackOverflow 调查中,它连续几年被评为最受欢迎的语言

除了作为一种出色的编程语言之外,Rust 还具有一个名为 Cargo 的构建系统和包管理器。Cargo 处理许多任务,例如构建代码、下载库或依赖项等等。两者捆绑在一起,因此您在安装 Rust 时会获得 Cargo。

安装 Rust 和 Cargo

在开始之前,您需要安装 Rust 和 Cargo。Rust 项目提供了一个可下载的脚本来处理安装。要获取脚本,请打开浏览器访问 https://sh.rustup.rs 并保存该文件。阅读脚本以确保您对它的意图感到满意,然后运行它

$ sh ./rustup.rs 

您也可以参考 安装 Rust 网页以获取更多信息。

安装 Rust 和 Cargo 后,您必须 source env 文件

$ source $HOME/.cargo/env

更好的是,将所需的目录添加到您的 PATH 变量中

$ source $HOME/.cargo/env

如果您更喜欢使用您的包管理器(例如 Linux 上的 DNF 或 Apt),请在您发行版的存储库中查找 Rust 和 Cargo 包并进行相应安装。例如

$ dnf install rust cargo

安装和设置完成后,验证您拥有的 Rust 和 Cargo 版本

$ rustc --version
rustc 1.41.0 (5e1a79984 2020-01-27)
$ cargo --version
cargo 1.41.0 (626f0f40e 2019-12-03)

手动构建和运行 Rust

从一个简单的程序开始,该程序在屏幕上打印“Hello, world!”。打开您喜欢的文本编辑器并键入以下程序

$ cat hello.rs 
fn main() {
    println!("Hello, world!");
}

使用 .rs 扩展名保存文件,以将其标识为 Rust 源代码文件。

使用 Rust 编译器 rustc 编译您的程序

$ rustc hello.rs

编译后,您将获得一个二进制文件,其名称与源程序相同

$ ls -l
total 2592
-rwxr-xr-x. 1 user group 2647944 Feb 13 14:14 hello
-rw-r--r--. 1 user group      45 Feb 13 14:14 hello.rs
$

执行您的程序以验证它是否按预期运行

$ ./hello 
Hello, world!

这些步骤对于较小的程序或当您想快速测试某些东西时就足够了。但是,当处理涉及多人的大型程序时,Cargo 是最佳选择。

使用 Cargo 创建新包

Cargo 是 Rust 的构建系统和包管理器。它可以帮助开发人员下载和管理依赖项,并协助创建 Rust 包。Rust 中的包在 Rust 社区中通常被称为“crates”,但在本文中,这两个词可以互换使用。有关说明,请参阅 Rust 社区提供的 Cargo FAQ

如果您需要任何关于 Cargo 命令行实用程序的帮助,请使用 --help-h 命令行参数

$ cargo –help

要创建新包,请使用 new 关键字,后跟包名称。对于此示例,请使用 hello_opensource 作为您的新包名称。运行命令后,您将看到一条消息,确认 Cargo 已创建具有给定名称的二进制包

$ cargo new hello_opensource
     Created binary (application) `hello_opensource` package

运行 tree 命令以查看目录结构报告,显示创建了一些文件和目录。首先,它创建了一个以包名称命名的目录,在该目录中有一个 src 目录用于您的源代码文件

$ tree .
.
└── hello_opensource
    ├── Cargo.toml
    └── src
        └── main.rs

2 directories, 2 files

Cargo 不仅创建了一个包,而且还创建了一个简单的 Hello, world! 程序。打开 main.rs 文件并查看一下

$ cat hello_opensource/src/main.rs 
fn main() {
    println!("Hello, world!");
}

下一个要处理的文件是 Cargo.toml,它是您包的配置文件。它包含有关包的信息,例如其名称、版本、作者信息和 Rust 版本信息。

程序通常依赖于外部库或依赖项才能运行,这使您可以编写执行您不知道如何编码或不想花时间编码的任务的应用程序。您的所有依赖项都将在此文件中列出。此时,您的新程序没有任何依赖项。打开 Cargo.toml 文件并查看其内容

$ cat hello_opensource/Cargo.toml 
[package]
name = "hello_opensource"
version = "0.1.0"
authors = ["user <user@mail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.net.cn/cargo/reference/manifest.html

[dependencies]

使用 Cargo 构建程序

到目前为止,一切顺利。现在您已经有了一个包,构建一个二进制文件(也称为可执行文件)。在此之前,请移动到包目录

$ cd hello_opensource/

您可以使用 Cargo 的 build 命令来构建包。请注意显示正在 Compiling 您的程序的消息

$ cargo build
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.38s

检查在您运行 build 命令后您的项目目录发生了什么变化

$ tree .
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    └── debug
        ├── build
        ├── deps
        │   ├── hello_opensource-147b8a0f466515dd
        │   └── hello_opensource-147b8a0f466515dd.d
        ├── examples
        ├── hello_opensource
        ├── hello_opensource.d
        └── incremental
            └── hello_opensource-3pouh4i8ttpvz
                ├── s-fkmhjmt8tj-x962ep-1hivstog8wvf
                │   ├── 1r37g6m45p8rx66m.o
                │   ├── 2469ykny0eqo592v.o
                │   ├── 2g5i2x8ie8zed30i.o
                │   ├── 2yrvd7azhgjog6zy.o
                │   ├── 3g9rrdr4hyk76jtd.o
                │   ├── dep-graph.bin
                │   ├── query-cache.bin
                │   ├── work-products.bin
                │   └── wqif2s56aj0qtct.o
                └── s-fkmhjmt8tj-x962ep.lock

9 directories, 17 files

哇!编译过程生成了许多中间文件。但是,您的二进制文件保存在 ./target/debug 目录中,其名称与您的包相同。

使用 Cargo 运行您的应用程序

现在您的二进制文件已构建完成,使用 Cargo 的 run 命令运行它。正如预期的那样,它在屏幕上打印 Hello, world!

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/hello_opensource`
Hello, world!

或者,您可以直接运行二进制文件,它位于

$ ls -l ./target/debug/hello_opensource
-rwxr-xr-x. 2 root root 2655552 Feb 13 14:19 ./target/debug/hello_opensource

正如预期的那样,它产生相同的结果

$ ./target/debug/hello_opensource
Hello, world!

假设您需要重建您的包并删除所有二进制文件和早期编译过程创建的中间文件。Cargo 提供了一个方便的 clean 选项,用于删除除源代码和其他必需文件之外的所有中间文件

$ cargo clean
$ tree .
.
├── Cargo.lock
├── Cargo.toml
└── src
    └── main.rs

1 directory, 3 files

对程序进行一些更改并再次运行它,看看它是如何工作的。例如,这个小的更改将 Opensource 添加到 Hello, world! 字符串中

$ cat src/main.rs 
fn main() {
    println!("Hello, Opensource world!");
}

现在,构建程序并再次运行它。这次您会看到 Hello, Opensource world! 显示在屏幕上

$ cargo build
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/hello_opensource`
Hello, Opensource world!

使用 Cargo 添加依赖项

Cargo 允许您添加程序运行所需的依赖项。使用 Cargo 添加依赖项非常容易。每个 Rust 包都包含一个 Cargo.toml 文件,其中包含(默认情况下为空)依赖项列表。在您喜欢的文本编辑器中打开该文件,找到 [dependencies] 部分,并添加您要包含在包中的库。例如,要将 rand 库添加为您的依赖项

$ cat Cargo.toml 
[package]
name = "hello_opensource"
version = "0.1.0"
authors = ["test user <test@mail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.net.cn/cargo/reference/manifest.html

[dependencies]
rand = "0.3.14"

尝试构建您的包,看看会发生什么。

$ cargo build
    Updating crates.io index
   Compiling libc v0.2.66
   Compiling rand v0.4.6
   Compiling rand v0.3.23
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 4.48s

Cargo 现在正在访问 Crates.io,它是 Rust 的 crates(或包)中央存储库,并下载和编译 rand。但是等等——libc 包呢?您没有要求安装 libc。好吧,rand 包依赖于 libc 包;因此,Cargo 也下载和编译 libc

库的新版本不断涌现,Cargo 提供了一种简单的方法来更新其所有依赖项,即使用 update 命令

cargo update

您也可以选择使用 -p 标志后跟包名称来更新特定的库

cargo update -p rand

使用单个命令编译和运行

到目前为止,每当您对程序进行更改时,您都会使用 build,然后使用 run。有一种更简单的方法:您可以简单地使用 run 命令,它会在内部编译并运行程序。要了解它是如何工作的,首先清理您的包目录

$ cargo clean
$ tree .
.
├── Cargo.lock
├── Cargo.toml
└── src
    └── main.rs

1 directory, 3 files

现在执行 run。输出表明它编译然后运行了程序,这意味着您不需要每次都显式运行 build

$ cargo run
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.41s
     Running `target/debug/hello_opensource`
Hello, world!

在开发中检查您的代码

在开发程序时,您经常会经历多次迭代。您需要确保您的程序没有编码错误并且编译正常。您不需要每次编译都生成二进制文件的开销。Cargo 为您提供了一个 check 选项,该选项会编译您的代码,但会跳过生成可执行文件的最后一步。

首先在您的包目录中运行 cargo clean

$ tree .
.
├── Cargo.lock
├── Cargo.toml
└── src
    └── main.rs

1 directory, 3 files

现在运行 check 命令,看看目录发生了哪些变化

$ cargo check
    Checking hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.18s

输出显示,即使中间文件是作为编译过程的一部分创建的,但最终的二进制文件或可执行文件并未创建。这节省了一些时间,如果包很大并且有数千行代码,这非常重要

$ tree .
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    └── debug
        ├── build
        ├── deps
        │   ├── hello_opensource-842d9a06b2b6a19b.d
        │   └── libhello_opensource-842d9a06b2b6a19b.rmeta
        ├── examples
        └── incremental
            └── hello_opensource-1m3f8arxhgo1u
                ├── s-fkmhw18fjk-542o8d-18nukzzq7hpxe
                │   ├── dep-graph.bin
                │   ├── query-cache.bin
                │   └── work-products.bin
                └── s-fkmhw18fjk-542o8d.lock

9 directories, 9 files

要查看您是否真的节省了时间,请对 buildcheck 命令进行计时并进行比较。

首先是 build 命令

$ time cargo build
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.40s

real	0m0.416s
user	0m0.251s
sys	0m0.199s

在运行 check 命令之前清理目录

$ cargo clean

check 命令

$ time cargo check
    Checking hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.15s

real	0m0.166s
user	0m0.086s
sys	0m0.081s

显然,check 命令要快得多。

构建外部 Rust 包

到目前为止,您所做的一切都适用于您从互联网上获得的任何 Rust crate。您只需下载或克隆存储库,移动到包文件夹,然后运行 build 命令,就可以了

git clone <github-like-url>
cd <package-folder>
cargo build

使用 Cargo 构建优化的 Rust 程序

到目前为止,您已经多次运行 build,但是您注意到它的输出了吗?没关系,再次构建它并仔细观察

$ cargo build
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished dev [unoptimized + debuginfo] target(s) in 0.36s

看到每次编译后的 [unoptimized + debuginfo] 文本了吗?这意味着 Cargo 生成的二进制文件包含大量调试信息,并且未针对执行进行优化。开发人员经常经历多次开发迭代,并且需要此调试信息进行分析。此外,性能不是开发软件的直接目标。因此,现在这样是可以的。

但是,一旦软件准备好发布,它就不再需要调试信息了。但它确实需要针对最佳性能进行优化。在开发的最后阶段,您可以将 --release 标志与 build 一起使用。仔细观察;您应该在编译后看到 [optimized] 文本

$ cargo build --release
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
    Finished release [optimized] target(s) in 0.29s

如果您愿意,您可以完成练习,以找出运行优化软件与未优化软件时节省的时间。

使用 Cargo 创建库与二进制文件

任何软件程序都可以大致分为独立二进制文件或库。独立二进制文件可以直接运行,即使它可能使用外部库。但是,库由另一个独立二进制文件使用。您到目前为止在本教程中构建的所有程序都是独立的二进制文件,因为那是 Cargo 的默认设置。要创建 ,请添加 --lib 选项

$ cargo new --lib libhello
     Created library `libhello` package

这次,Cargo 不会创建 main.rs 文件;相反,它会创建一个 lib.rs 文件。您的库的代码应该放在这里

$ tree .
.
└── libhello
    ├── Cargo.toml
    └── src
        └── lib.rs

2 directories, 2 files

了解 Cargo 后,不要惊讶于它在您的新库文件中放入了一些代码。通过移动到包目录并查看文件来找出它添加了什么。默认情况下,Cargo 将测试函数放在库文件中。

使用 Cargo 运行测试

Rust 为单元测试和集成测试提供了一流的支持,Cargo 允许您执行任何这些测试

$ cd libhello/

$ cat src/lib.rs 
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

Cargo 有一个方便的 test 选项来运行代码中存在的任何测试。尝试运行 Cargo 默认放在库代码中的测试

$ cargo test
   Compiling libhello v0.1.0 (/opensource/libhello)
    Finished test [unoptimized + debuginfo] target(s) in 0.55s
     Running target/debug/deps/libhello-d52e35bb47939653

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests libhello

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

了解 Cargo 的幕后工作原理

您可能对了解 Cargo 在您运行命令时在幕后做了什么感兴趣。毕竟,Cargo 在许多方面都是一个包装器。要了解它在做什么,您可以将 -v 选项与任何 Cargo 命令一起使用,以将详细信息输出到屏幕。

以下是使用 -v 选项运行 buildclean 的几个示例。

build 命令中,您可以看到底层的 rustc(Rust 编译器)使用给定的命令行选项启动

$ cargo build -v
   Compiling hello_opensource v0.1.0 (/opensource/hello_opensource)
     Running `rustc --edition=2018 --crate-name hello_opensource src/main.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=147b8a0f466515dd -C extra-filename=-147b8a0f466515dd --out-dir /opensource/hello_opensource/target/debug/deps -C incremental=/opensource/hello_opensource/target/debug/incremental -L dependency=/opensource/hello_opensource/target/debug/deps`
    Finished dev [unoptimized + debuginfo] target(s) in 0.36s

clean 命令显示它只是删除了包含中间文件和二进制文件的目录

$ cargo clean -v
    Removing /opensource/hello_opensource/target

不要让你的技能生疏

要扩展您的技能,请尝试使用 Rust 和 Cargo 编写和运行稍微复杂的程序。简单的事情就可以:例如,尝试列出当前目录中的所有文件(可以用九行代码完成),或者尝试将输入回显给自己。小的实践应用程序可以帮助您熟悉语法以及编写和测试代码的过程。

本文为刚入门的 Rust 程序员提供了充足的信息,以开始使用 Cargo。但是,当您开始处理更大、更复杂的程序时,您将需要更深入地了解 Cargo。当您准备好了解更多信息时,请下载并阅读 Rust 团队编写的开源 Cargo Book,看看您可以创造什么!

接下来阅读什么
标签
User profile image.
经验丰富的软件工程专业人士。主要兴趣是安全、Linux、恶意软件。喜欢在命令行工作。对底层软件和理解事物的工作原理感兴趣。此处表达的观点仅代表我个人,不代表我的雇主

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.