Rust基础——模块化
开始
rust的模块化包含以下概念
- 包:一个包包含多个crates,每个包都有一个
Cargo.toml
文件,一个包可以看做一个项目 - crates:一个crates是一个编译单元,可以是一个可执行文件或一个库
- 模块:一个模块表示一个命名空间,一个crates中包含多个模块,可以控制模块的可访问性
- 路径:访问一个模块或模块成员的方式,使用
::
作为分隔符
包和crate
crate有两种形式
- 二进制项:二进制项可以被编译为一个可执行文件,必须包含一个
main
函数 - 库:库没有
main
函数,其中定义一些类型或函数,可供其他crate使用
一个包可以看做一个项目,使用cargo new
命令创建,其中包含一个Cargo.toml
文件,用于管理依赖
一个包中至少包含一个crate,最多包含一个库crate,可以包含任意多个二进制crate
每个crate都至少有一个根模块crate
,也称为crate根,cargo有以下默认约定
src/main.rs
:与包同名的二进制crate根src/lib.rs
:与包同名的库crate根src/bin
:存放其他的二进制crate
模块
一个模块可以看做一个命名空间
- 使用
mod
关键字声明一个子模块 - 使用
pub
关键字控制模块的可访问性 - 使用
use
关键字引入模块
模块组织形式
一个crate中的模块被组织成一个树形结构,支持内联和多文件两种形式
内联:使用
mod
关键字声明一个模块后,紧跟一个代码块,在代码块中编写模块的代码1 2 3 4 5 6 7 8 9 10
// 通过crate::module访问module模块 // 通过crate::module::submodule访问子模块 mod module { struct Tuple(i32, i32); fn foo(x: i32, y: i32) {} mod submodule { fn foo(x: i32, y: i32) {} } }
多文件:利用文件树的形式,在父模块中使用
mod
关键字声明一个子模块,cargo会在子目录中查找子模块1 2 3 4 5
src ├──module // 与module.rs同名 │ └──submodule.rs ├──main.rs └──module.rs
main.rs
中使用mod module;
声明module子模块,cargo会查找module.rs
module.rs
中使用mod submodule;
声明submodule子模块,cargo会查找module/submodule.rs
- 使用
crate::module
访问module模块,使用crate::module::submodule
访问submodule模块
内联和多文件两种形式可以混合使用
将submodule.rs
中的模块代码以内联形式移动到module.rs
中
1
2
3
4
5
6
7
// 依然使用crate::module::submodule访问submodule
mod submodule {
fn foo(x: i32, y: i32) {}
}
struct Tuple(i32, i32);
fn foo(x: i32, y: i32) {}
模块可访问性
在默认情况下,模块内的成员是私有的,一个模块可以直接访问模块内的成员,子模块可以访问父模块中的成员
1
2
3
4
5
6
7
8
9
10
mod submodule {
fn foo(x: i32) {
super::foo(3); // 使用super关键字引用父模块
}
}
struct Structure;
fn foo(x: i32) {
let s = Structure; // 可直接访问模块内的成员
submodule::foo(2); // 不合法,可以直接访问submodule,但无法访问submodule的成员foo
}
使用pub
关键字将其变为公有
1
2
3
4
5
pub mod submodule {
fn foo(x: i32) {}
}
pub struct Structure;
pub fn foo(x: i32) {}
此时,submodule
子模块、Structure
结构体和foo
函数作为模块内的成员,对外部是可见的,但submodule
子模块内的成员依然对submodule
是私有的,若需要外部访问submodule
子模块内的成员,依然使用pub
关键字修饰
1
2
3
4
5
pub mod submodule {
pub fn foo(x: i32) {}
}
pub struct Structure;
pub fn foo(x: i32) {}
pub
关键字还可以控制结构体成员和枚举成员的可访问性
结构体成员默认为私有的,可以用
pub
修饰变为公有1 2 3 4 5 6 7 8 9 10 11 12 13 14
pub struct Breakfast { pub toast: String, seasonal_fruit: String, // seasonal_fruit为私有字段 } impl Breakfast { // 公有关联函数 pub fn summer(toast: &str) -> Breakfast { Breakfast { toast: String::from(toast), seasonal_fruit: String::from("peaches"), } } }
枚举成员默认为私有的,一旦枚举类型用
pub
修饰变为公有,则所有成员变为公有1 2 3 4
pub enum Appetizer { Soup, Salad, }
引入模块
绝对路径与相对路径
使用use
关键字引入一个模块或模块成员,在引入时的路径格式为A::B::C
,有两种形式
绝对路径:以根模块
crate
开始1
use crate::module::submodule;
相对路径:默认以当前模块开始,
self
关键字表示当前模块,super
关键字表示父模块1 2 3 4
// 当前模块为src/module.rs use submodule::foo; // 对应绝对路径为crate::module::submodule::foo use self::submodule; // 对应绝对路径为crate::module::submodule use super::other_module; // 对应绝对路径为crate::other_module
use
关键字
使用use
引入的惯用做法
对于模块中的函数,引入到模块级别
1 2 3 4 5 6 7
mod front_house { pub mod hosting { pub fn add_waitlist() {} } } use front_house::hosting; // 调用hosting::add_waitlist();
对于模块中的类型,引入到成员级别
1 2 3 4 5 6 7
mod front_house { pub mod hosting { pub struct House; } } use front_house::hosting::House; // 直接使用House类型
use
引入的模块或模块成员只在当前模块中可见
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mod front_house {
pub mod hosting {
pub fn add_waitlist() {}
}
}
use front_house::hosting;
// 只作用在当前模块中
fn foo() {
hosting::add_waitlist();
}
mod customer {
pub fn eat() {
hosting::add_waitlist(); // 不合法,在customer模块中不能直接访问引入的hosting
}
}
若需要外部访问当前模块中引入的模块或模块成员,可以使用pub use
重导出
1
pub use front_of_house::hosting;
当引入发生名称冲突时,使用as
关键字为某个名称起别名
1
2
use std::fmt::Result;
use std::io::Result as IoResult;
use
引入支持嵌套路径和通配符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
mod front_house {
pub mod hosting {
pub struct House;
pub mod serving {
pub fn serve() {}
}
}
pub fn foo() {}
pub mod eating {
pub fn eat() {}
}
}
use front_house::{
foo, // 引入foo函数
eating::*, // 使用通配符引入eating模块中的所有公有成员,不包含eating本身
// 嵌套路径
hosting::{
self, // 使用self关键字引入hosting模块本身
House, // 引入House结构体
serving, // 引入子模块
}
};
注意,在直接路径中,
self
和super
只能用于路径开头,而在嵌套路径中,self
多了一个引入某一级模块本身的功能