13_rust的模块系统,Package、Crate,定义Module,路径(Path)
rust的模块系统
代码组织包括:哪些细节对外暴露,哪些细节私有,作用域哪些名称有效等。
rust的模块系统:
- 最上层 Package(包):Cargo的特性,构建、测试、共享crate。
- 第二层 Crate(单元包):一个模块树,可产生一个Library或可执行文件。
- 第三层Module(模块)、use:控制代码的组织、作用域、私有路径。
- Path(路径):为struct、function或module等项命名的方式。
pacakge和Crate
Crate的类型:binary、library。
Crate Root:源代码文件,rust编译器的开始之处,组成crate的根module。
一个Package:
- 包含1个Cargo.toml,描述了如何构建Crates(可能多个crate)。
- 只能包含0-1个library crate。
- 可包含任意数量的binary crate。
- 但必须至少包含一个crate(library或binary)。
可通过命令行创建一个Package:
$ cargo new test_prj
Created binary (application) "test_prj" package
cd test_prj
code . # 使用vs code打开
可看到有配置文件Cargo.toml和源文件src/main.rs,但配置文件里并未提到main.rs文件,这是因Cargo惯例,默认是binary.crate的crate root。
Cargo的惯例
src/main.rs:
- binary crate的crate root
- crate名与package名相同
src/lib.rs:
- package包含一个library crate
- library crate的crate root
- crate名与package名相同
如果有main.rs就说明有一个binary crate,如果有lib.rs则说明有一个library crate。Carge把crate root文件交给rustc来构建library或binary。
1)一个Package可同时包含src/main.rs和src/lib.rs
- 一个binary crate、一个library crate,名称都与package名相同
2)一个Package可有多个binary crate - 文件放在src/bin下
- 每个文件是单独的binary crate
crate的作用是将相关功能组合到一个作用域内,便于项目间进行共享以防止冲突,如rand crate,访问其功能需要通过rand名称来访问。
定义module
定义module来控制作用域和私有性。
Module:
- 在一个crate内,将代码进行分组。
- 增加可读性,易于复用。
- 控制项目(item)的私有性,public、private。
建立module:
- mod关键字
- 可嵌套
- 可含其它项(struct、enum、常量、trait、函数等)的定义
mod high_modu { // 父moduler,顶层Moduler
mod test1 { // 子moduler 1
fn test_func() {}
fn test_2() {}
}
mod test2 { // 子moduler 2
fn test3() {}
}
}
src/main.rs和src/lib.rs叫做crate.root,这两个文件(任意一个)的内容形成了名为crate的模块,位于整个模块树的根部。
crate
|- high_modu
|- test1
|- ...
|- test2
|- ...
路径(Path)
要找到模块里的内容,必须知道路径才行,与文件系统里的概念类似。
1)为了在rust的模块中找到某个条目,需要使用路径。
2)路径有两种形式:
- 绝对路径:从crate root开始,使用crate名或字面值crate。
- 相对路径:从当前模块开始,使用self(自身)、super(上一级)或当前模块的标识符
3)路径至少由一个标识符组成,标识符之间使用::
符号
如:在一项目中创建一个src/lib.rs,内容如下:
mod test_modu1 { // lib.rs的内容隐式组成了名为crate的模块,所以绝对路径就从crate开始。
mod test_modu2 {
fn add_num() {}
}
}
pub fn test_func() { // 测试调用test_modu2里的函数,与test_modu1在同一个crate里。
crate::test_modu1::test_modu2::add_num(); // 绝对路径调用,从crate开始
test_modu1::test_modu2::add_num(); // 使用相对路径调用Moduler内函数,因为在同一个crate里,与test_modu1同级
}
私有边界(private boundary)
模块不仅可组织代码,还可定义私有边界,如果项把函数或struct等设为私有,可将其放在某个模块中。
rust中所有条目(函数、方法、struct、enum、模块、常量)默认是私有的。
另外:(类似于作用域的概念)
- 父级模块无法访问子模块中的私有条目
- 子模块里可使用所有祖先模块中的条目
使用pub关键字来将某些条目标记为公共的。
上面的例子需要做如下修改
mod test_modu1 { // 因为在同一文件,都属于根节点,类似与同文件内的函数,可被同级调用
pub mod test_modu2 { // 加pub关键字后才能被外部调用
pub fn add_num() {}
}
}
pub fn test_func() { // 测试调用test_modu2里的函数
crate::test_modu1::test_modu2::add_num();
test_modu1::test_modu2::add_num();
}
super关键字
super:用来访问父级模块路径中的内容,类似文件系统中的../操作
fn test_super() {}
mod modu1 {
fn test1() {
test2();
super::test_super();
crate::test_super();
}
fn test2() {}
}
pub struct
在struct关键字前加pub关键字,struct为公共的,但struc的字段默认还是私有的。struct的字段需要单独设pub才会是公共的。
mod modu1 {
pub struct st1 {
pub str1: String, // 公共
num1: i32, // 私有
}
impl st1 {
pub fn test_func(str2: &str) { // 定义一关联函数
st1 { // 返回值是st1的实例
str1: String::from(str2),
num1: 5,
}
}
}
}
pub fn test_func2() {
let mut st1_obj = modu1::st1::test_func("test1");
st1_obj.st1 = String::from("test2"); // 可修改,因为是公共的
st1_obj.num1 = 9; // 编译报错,不可访问私有字段
}
pub enum
定义与struct一样,在enum前加pub关键字,但加了之后,enum本身是公共的,enum的变体也都是公共的。
因为enum的定义目的就是使用其内的枚举,如果不是公共的也就没意义了,不像struct常需要有私有字段。