Rust 学习之旅(7):Package,Crate,Module

Rust 学习之旅(7):Package,Crate,Module

这是第 7 章的读书笔记,Cargo Workspace 在第 14 章。

Packages and Crates

As a project grows, you should organize code by splitting it into multiple modules and then multiple files. A package can contain multiple binary crates and optionally one library crate. As a package grows, you can extract parts into separate crates that become external dependencies.

随着项目的增长,你需要通过把代码分离到不同的模块,不同的文件来管理代码。一个包(Package)可以有多个可执行文件(Binary Crate),和一个可选的 Library Crate。随着这个包的增长,还可以把包的一部分变成一个外部依赖。

Rust 的模块系统(Module System)有很多的特性:

  • Packages: A Cargo feature that lets you build, test, and share crates
  • Crates: A tree of modules that produces a library or executable
  • Modules and use: Let you control the organization, scope, and privacy of paths
  • Paths: A way of naming an item, such as a struct, function, or module

A crate is the smallest amount of code that the Rust compiler considers at a time. Even if you run rustc rather than cargo and pass a single source code file (as we did all the way back in the “Writing and Running a Rust Program” section of Chapter 1), the compiler considers that file to be a crate. Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections.

Rust 编译器一次考虑一个 Crate。如果使用 rustc 编译单个 .rs 文件,编译器会认为这个 .rs 文件就是一个 Crate。Crate 里面可以有很多的模块,这些模块可以在不同的文件里面。一个 Crate 可以是一个可执行的文件(Binary Crate)或者库(Library Crate)。Binary Crate 必须有一个 main 函数,Library Crate 不能有 main 函数。

A crate can come in one of two forms: a binary crate or a library crate. Binary crates are programs you can compile to an executable that you can run, such as a command-line program or a server. Each must have a function called main that defines what happens when the executable runs.

Library crates don’t have a main function, and they don’t compile to an executable. Instead, they define functionality intended to be shared with multiple projects. For example, the rand crate we used in Chapter 2 provides functionality that generates random numbers. Most of the time when Rustaceans say “crate”, they mean library crate, and they use “crate” interchangeably with the general programming concept of a “library".

Rustacean 所说的这个 Crate,就相当于说是库。

The crate root is a source file that the Rust compiler starts from and makes up the root module of your crate.

A package is a bundle of one or more crates that provides a set of functionality. A package contains a Cargo.toml file that describes how to build those crates. Cargo is actually a package that contains the binary crate for the command-line tool you’ve been using to build your code. The Cargo package also contains a library crate that the binary crate depends on. Other projects can depend on the Cargo library crate to use the same logic the Cargo command-line tool uses.

一个包就是一个或者多个 Crate 绑在一起。一个 Package 包含一个 Cargo.toml 描述如何去构建这些 crate。Cargo 实际上是一个包,Cargo 有作为命令行工具它得提供 Binary Crate,此外 Cargo 也包含了它的 Binary Crate 所依赖的 Library Crate。

A package can contain as many binary crates as you like, but at most only one library crate. A package must contain at least one crate, whether that’s a library or binary crate.

一个包,就是我们用 cargo 创建的项目,可以有很多的 Binary Crate,也就是说我们新建了一个项目,我们可以构建多个可执行的文件,但是最多只能有一个 Library Crate。我们创建了一个包,当然是希望这个包是一个库或者这个包是可以为我们生成可执行的文件,所以我们创建一个包,这个包至少得有一个 Crate,要么是一个 Binary Crate,要么是 Library Crate。

一个 Package 里面有我们的依赖和库,依赖是 Crate,自己写的一个或者好几个代码文件组成的也是一个 Crate。当我们使用 cargo 创建一个项目的时候,它会提示说 Created ... package。所以我们创建一个项目的时候实际上就是新建了一个包。

$ cargo new my-project
     Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs

我们常说 C/C++ 的编译器一次考虑一个编译单元,而 Rust 一次考虑一个 Crate,从 Crate Root 开始找这个 Crate 要哪些东西。

In the project directory, there’s a Cargo.toml file, giving us a package. There’s also a src directory that contains main.rs. Open Cargo.toml in your text editor, and note there’s no mention of src/main.rs. Cargo follows a convention that src/main.rs is the crate root of a binary crate with the same name as the package. Likewise, Cargo knows that if the package directory contains src/lib.rs, the package contains a library crate with the same name as the package, and src/lib.rs is its crate root. Cargo passes the crate root files to rustc to build the library or binary.

Cargo.toml 里面没有提到 src/main.rs。Cargo 遵循一个惯例:src/main.rs 是默认的 Binary Crate 的 crate root,这个 Binary Crate 的名字和包的名字一样。src/lib.rs 是默认的 Library Crate 的 crate root,这个 Library Crate 的名字和包的名字一样。Cargo 把 crate root 文件传给 rustc 去构建库或者可执行的。

If a package contains src/main.rs and src/lib.rs, it has two crates: a binary and a library, both with the same name as the package. A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.

一个包里面有 src/main.rssrc/lib.rs,意味着这个包有两个 crate,分别是 binary crate 和 library crate。这俩 crate 的名字都是包的名字。一个包可以有多个 binary crate 放在 src/bin 目录下。每个文件都是一个单独的 binary crate。

7.1 小结

cargo 创建的项目实际上就是一个包,一个项目里面可以依赖别的库,Rustacean 通常把库称作 crate。我们创建一个项目可以生产多个可执行的文件,src/main.rs 是默认和包名(项目名)同名的可执行文件。其余的可执行文件的代码可以放在 src/bin 目录下。crate 并不是单个代码文件,crate 是 Rust 编译器一次考虑的代码们。Crate 可能是涉及到多个代码文件的。不过一个包里面只能有一个库,我们的 package 里面只能有一个 library crate。我们通常就是在造 Crate,如果希望执行一个 Crate 做些事情就造一个 Binary Crate,如果希望造个 Crate 给别人用就造个 Library Crate。

Defining Modules to Control Scope and Privacy

第 7 章讲模块系统,当然是要有模块的。模块系统就是控制作用域和名字空间。

Modules Cheat Sheet

Here we provide a quick reference on how modules, paths, the use keyword, and the pub keyword work in the compiler, and how most developers organize their code. We’ll be going through examples of each of these rules throughout this chapter, but this is a great place to refer to as a reminder of how modules work.

  • Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate or src/main.rs for a binary crate) for code to compile.
  • Declaring modules: In the crate root file, you can declare new modules; say, you declare a “garden” module with mod garden;. The compiler will look for the module’s code in these places:
    • Inline, within curly brackets that replace the semicolon following mod garden
    • In the file src/garden.rs
    • In the file src/garden/mod.rs
  • Declaring submodules: In any file other than the crate root, you can declare submodules. For example, you might declare mod vegetables; in src/garden.rs. The compiler will look for the submodule’s code within the directory named for the parent module in these places:
    • Inline, directly following mod vegetables, within curly brackets instead of the semicolon
    • In the file src/garden/vegetables.rs
    • In the file src/garden/vegetables/mod.rs
  • Paths to code in modules: Once a module is part of your crate, you can refer to code in that module from anywhere else in that same crate, as long as the privacy rules allow, using the path to the code. For example, an Asparagus type in the garden vegetables module would be found at crate::garden::vegetables::Asparagus.
  • Private vs public: Code within a module is private from its parent modules by default. To make a module public, declare it with pub mod instead of mod. To make items within a public module public as well, use pub before their declarations.
  • The use keyword: Within a scope, the use keyword creates shortcuts to items to reduce repetition of long paths. In any scope that can refer to crate::garden::vegetables::Asparagus, you can create a shortcut with use crate::garden::vegetables::Asparagus; and from then on you only need to write Asparagus to make use of that type in the scope.

除了 Crate 之外,其他的代码文件都是模块(Module)。使用文件系统类似的路径的组织模块,模块可以直接写在一个代码文件里面,也可以放在单独的文件,大一点的模块可以放在一个目录里面。

Modules let us organize code within a crate for readability and easy reuse. Modules also allow us to control the privacy of items, because code within a module is private by default. Private items are internal implementation details not available for outside use. We can choose to make modules and the items within them public, which exposes them to allow external code to use and depend on them.

一个 crate 内的代码,通过模块化的组织可以增强可读性,更容易重用。模块允许我们控制私有的项,因为模块内的代码默认就是私有的(private)。私有代码的实现细节是不对外公开的。我们可以选择公开模块内的代码,把模块内的代码暴露给外部使用。

整个模块树的根有个名字:crate。

7.2 小结

我们的目的是生产 Crate,为了更好的管理和生产 Crate,我们把 Crate 相关的东西划分成模块。就像组装木条箱子,我们整理好木条箱子的上下的板子,再整理好前后左右的板子再拼接起来。至于那块板子是铁的还是木头的,有没有加块布或者皮包起来不重要,重要的是,我们要组装箱子,我们拿箱子的各个部分来组装就行了。(我也没干过木工,不知道咋干的)

Paths for Referring to an Item in the Module Tree

A path can take two forms:

  • An absolute path is the full path starting from a crate root; for code from an external crate, the absolute path begins with the crate name, and for code from the current crate, it starts with the literal crate.
  • A relative path starts from the current module and uses self, super, or an identifier in the current module.

super 引用父级作用域,crate 是 Crate Root。

7.3 和 7.4 小节主要是用法,直接看书上的例子就行。

Separating Modules into Different Files

7.5 小节讲如何把一个大的模块分到不同文件。

Note that you only need to load a file using a mod declaration once in your module tree. Once the compiler knows the file is part of the project (and knows where in the module tree the code resides because of where you’ve put the mod statement), other files in your project should refer to the loaded file’s code using a path to where it was declared, as covered in the “Paths for Referring to an Item in the Module Tree” section. In other words, mod is not an “include” operation that you may have seen in other programming languages.

在 module tree 里面加载一个模块只需要用 mod 声明一次,其他的文件引用这个模块使用声明的路径。换句话说,mod 不是其他语言里面的 include。其他的文件要引用这个模块是通过这个模块树的路径,而不是把这个模块重新 include 一次。C++ 20 新特性 Modules。

7.5小结

最重要的是弄清楚 Rust 模块系统的术语。Rust 可以把一个包分成多个 Crate,一个 Crate 可以分成多个模块,模块之间可以互相引用。模块代码默认是私有的,可以使用 pub 关键字公开。

posted @ 2022-10-26 19:07  wngtk  阅读(260)  评论(0编辑  收藏  举报