- 包(Packages):Cargo 的一个功能,可以用来构建、测试和分享 crate。
- Crate :一个由多个模块组成的树形结构,可以作为三方库进行分发,也可以生成可执行文件进行运行,crate 是 Rust 在编译时最小的代码单位。
- Crates :一个模块的树形结构,它形成了库或二进制项目。
- 模块(Modules)和 use:允许你控制作用域和路径的私有性。
- 路径(path):一个命名例如结构体、函数或模块等项的方式。
包、crate
- crate 分为两种形式,二进制和库。
- 二进制:可以编译为可执行程序,它必须有一个
main函数。 - 库:没有
main函数,也不能编译为可执行程序,只是提供一些函数之类的东西。可以将其理解为其他编程语言中的 library 类似的概念。
- 包(package)是提供一系列功能的一个或者多个 crate。可以理解为工程、软件包。一个包会包含一个 Cargo.toml 文件,描述如何去构建这些 crate。
二进制 Package
创建一个二进制 package,就是之前介绍过的创建 hello_world 项目:
src/main.rs是二进制包的根文件,所有的代码执行都从该文件中的fn main()函数开始。
库 Package
库的创建需要在后面加上 --lib 参数:
$ cargo new my-lib --lib
Created library `my-lib` package
$ ls my-lib
Cargo.toml
src
$ ls my-lib/src
lib.rs
库类型的
Package只能作为三方库被其它项目引用,而不能独立运行,如果一个Package包含有src/lib.rs,该包的根文件是src/lib.rs。
Package 结构
在真实项目中的 Package,会包含多个二进制包,这些包文件被放在 src/bin 目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个 src/lib.rs:
.
├── Cargo.toml
├── Cargo.lock
├── src
│ ├── main.rs
│ ├── lib.rs
│ └── bin
│ └── main1.rs
│ └── main2.rs
├── tests
│ └── some_integration_tests.rs
├── benches
│ └── simple_bench.rs
└── examples
└── simple_example.rs
- 唯一库包:
src/lib.rs - 默认二进制包:
src/main.rs,编译后生成的可执行文件与Package同名 - 其余二进制包:
src/bin/main1.rs和src/bin/main2.rs,它们会分别生成一个文件同名的二进制可执行文件 - 集成测试文件:
tests目录下 - 基准性能测试
benchmark文件:benches目录下 - 项目示例:
examples目录下
模块 Module
Rust中的模块主要用于组织代码,通过把功能相近的代码组织在一起,可以提高代码的可读性和可维护性。另外,模块还可以隐藏内部实现细节,为使用者提供一定的代码封装。
- crate 根节点:编译crate 时,编译器先在crate 跟文件(src/main.rs 、src/lib.rs)寻找需要编译的代码。
- 声明模块:需要使用
mod关键字来声明模块,例如mod garden声明的gargen模块,编译器会在下面的路径中寻找对应的代码:- 内联,
mod garden后面的大括号中 - src/garden.rs
- src/garden/mod.rs
- 内联,
- 私有与公用:一个模块里的代码默认对其父模块私有。为了使一个公用模块内部的成员公用,应当在声明前使用
pub。 use关键字:使用一个成员的快捷方式。例如crate::a::b::c的作用域,你可以通过use crate::a::b::c;创建一个快捷方式,然后你就可以在作用域中只写c来使用该类型。
- 模块的语法:在Rust中,我们使用
mod关键字来定义一个模块,语法如下:
mod module_name {
// 模块内容
}
模块的内容可以包含函数、结构体、枚举等。
2. 使用模块:使用模块时,需要通过 use 关键字引入需要的模块,例如:
use module_name::function_name;
这样就可以在当前的代码中使用 function_name。
3. 模块的层级:Rust中的模块可以有层级,即一个模块可以包含另一个模块。例如:
mod a {
mod b {
// b模块的内容
}
}
在这种情况下,我们可以使用 a::b::function_name 的方式来访问 b 模块中的函数。
- 引用模块项目的路径
路径有两种形式:
- 绝对路径(absolute path)是以 crate 根(root)开头的全路径;对于外部 crate 的代码,是以 crate 名开头的绝对路径,对于当前 crate 的代码,则以字面值
crate开头。 - 相对路径(relative path)从当前模块开始,以
self、super或当前模块的标识符开头。
绝对路径和相对路径都后跟一个或多个由双冒号(::)分割的标识符。
文件名:src/lib.rs
mod a {
pub mod b {
pub fn function_c() {}
}
}
pub fn test() {
// 绝对路径
crate::a::b::function_c();
// 相对路径
a::b::function_c();
}
虽然
a模块不是公有的,不过因为它与test函数定义于同一模块中(即,a和test是兄弟),我们可以从test中引用a。然后使用pub标记的b模块以及function_c函数,以便我们可以访问。
self关键字:当我们需要在一个模块中引用该模块时,可以使用 self 关键字,例如:
mod a {
fn function_a() {
println!("Function A");
}
fn function_b() {
println!("Function B");
self::function_a(); // 使用self关键字引用本模块的function_a
}
}
super关键字:当我们需要在一个子模块中引用其父模块时,可以使用 super 关键字,例如:
mod parent {
fn function_parent() {
println!("Parent function");
}
}
mod child {
use super::parent; // 引用父模块
fn function_child() {
println!("Child function");
parent::function_parent(); // 使用super关键字引用父模块的function_parent
}
}
-
Rust标准库中的模块:Rust标准库中包含了很多有用的模块,例如
std::collections模块中提供了各种常用的数据结构,如Vec、String、HashMap等。我们可以直接使用这些模块中的函数和类型。 -
自定义模块的导出:我们可以使用
pub关键字来导出模块中的函数或类型,例如:
mod my_module {
pub fn function_a() {
println!("Function A");
}
}
这样我们就可以在其他地方使用 my_module::function_a() 来访问这个函数。