包和单元库
我们将介绍的模块系统的第一个部分是包和单元库。
单元库 是 Rust 编译器一次考虑的最小代码量。即使您运行的是 rustc
而不是 cargo
,并且传递的是单个源代码文件(就像我们在第 1 章“编写和运行 Rust 程序”一节中所做的那样),编译器也会将该文件视为一个单元库。单元库可以包含模块,并且这些模块可以定义在与单元库一起编译的其他文件中,我们将在接下来的章节中看到。
单元库可以采用以下两种形式之一:二进制单元库或库单元库。二进制单元库 是可以编译成可执行程序的程序,例如命令行程序或服务器。每个二进制单元库都必须有一个名为 main
的函数,该函数定义了可执行文件运行时会发生什么。到目前为止,我们创建的所有单元库都是二进制单元库。
库单元库 没有 main
函数,并且它们不会编译成可执行文件。相反,它们定义了旨在与多个项目共享的功能。例如,我们在第 2 章中使用的 rand
单元库提供了生成随机数的功能。大多数情况下,当 Rustaceans 说“单元库”时,他们指的是库单元库,并且他们将“单元库”与“库”的一般编程概念互换使用。
单元库根 是 Rust 编译器开始编译的源文件,它构成了单元库的根模块(我们将在“定义模块以控制作用域和隐私”一节中详细解释模块)。
包 是一组提供一组功能的一个或多个单元库。包包含一个 Cargo.toml 文件,该文件描述了如何构建这些单元库。Cargo 实际上是一个包,其中包含用于构建代码的命令行工具的二进制单元库。Cargo 包还包含一个二进制单元库所依赖的库单元库。其他项目可以依赖 Cargo 库单元库来使用 Cargo 命令行工具使用的相同逻辑。
一个包可以包含任意数量的二进制单元库,但最多只能包含一个库单元库。一个包必须至少包含一个单元库,无论是库单元库还是二进制单元库。
让我们来看看创建包时会发生什么。首先,我们输入命令 cargo new
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
在运行 cargo new
命令后,我们使用 ls
命令查看 Cargo 创建的内容。在项目目录中,有一个 Cargo.toml 文件,它定义了一个包。还有一个 src 目录,其中包含 main.rs 文件。在文本编辑器中打开 Cargo.toml 文件,你会注意到其中没有提到 src/main.rs。Cargo 遵循一个约定:src/main.rs 是与包同名的二进制 crate 的 crate 根。同样,Cargo 知道如果包目录包含 src/lib.rs 文件,则该包包含一个与包同名的库 crate,而 src/lib.rs 是其 crate 根。Cargo 将 crate 根文件传递给 rustc
来构建库或二进制文件。
在这里,我们有一个只包含 src/main.rs 的包,这意味着它只包含一个名为 my-project
的二进制 crate。如果一个包同时包含 src/main.rs 和 src/lib.rs,则它有两个 crate:一个二进制 crate 和一个库 crate,它们都与包同名。一个包可以通过在 src/bin 目录中放置文件来拥有多个二进制 crate:每个文件都将是一个独立的二进制 crate。