rust- 定义模块以控制作用域和隐私
定义模块以控制作用域和隐私
本节中,我们将讨论模块及模块系统的其他部分,即路径(允许你为项命名);use 关键字(将路径引入作用域);以及 pub 关键字(使项公开)。我们还会讨论 as 关键字、外部包和通配符操作符。
模块速查表
在详细介绍模块和路径之前,这里提供一个关于编译器中模块、路径、use 和 pub 关键字如何工作的快速参考,以及大多数开发者如何组织代码。本章将通过示例逐条讲解这些规则,但这里是回顾模块工作原理的好地方。
-
从 crate 根开始:
编译 crate 时,编译器首先查看 crate 根文件(通常库 crate 是 src/lib.rs,二进制 crate 是 src/main.rs)中的代码进行编译。 -
声明模块:
在 crate 根文件中,你可以声明新模块;例如声明一个 “garden” 模块使用 mod garden; 。编译器会在以下位置寻找该模块的代码:- 内联,在用花括号替代 mod garden 后分号的位置内写代码块
- 文件 src/garden.rs 中
- 文件 src/garden/mod.rs 中
-
声明子模块:
在除根文件之外的任何文件中,都可以声明子模块。例如,可以在 src/garden.rs 中声明 mod vegetables; 。编译器会在父级同名目录下寻找子模块代码,具体位置包括:- 内联,在用花括号替代 mod vegetables 后分号的位置内写代码块
- 文件 src/garden/vegetables.rs 中
- 文件 src/garden/vegetables/mod.rs 中
-
访问模块中的代码路径:
一旦某个模块成为你的 crate 的一部分,只要隐私规则允许,就可以从同一crate中的任意位置通过该代码的路径引用它。例如,garden::vegetables 模块里的 Asparagus 类型可通过 crate::garden::vegetables::Asparagus 来访问。 -
私有与公共:
默认情况下,一个模组内部的代码对其父模组是私有的。若想让一个模组变成公共,则需使用 pub mod 而非 mod 声明。同样,要让公共模组内的项也变成公有,需要在它们前面加上 pub 修饰符。 -
use 关键字:
在某个作用域内,use 用于创建项目快捷方式,以减少长路径重复。在任何能引用到 crate::garden::vegetables::Asparagus 的作用域里,可以用 use crate::garden::vegetables::Asparagus; 创建快捷方式,此后只需写 Asparagus 即可使用该类型。
下面举例说明以上规则,我们创建了一个名为 backyard 的二进制crate,其目录结构如下:
backyard
├── Cargo.lock
├── Cargo.toml
└── src├── garden│ └── vegetables.rs├── garden.rs└── main.rs
此处crate根文件是src/main.rs,其内容如下:
Filename: src/main.rs
use crate::garden::vegetables::Asparagus;pub mod garden;fn main() {let plant = Asparagus {};println!("I'm growing {plant:?}!");
}
pub mod garden; 告诉编译器包含位于src/garden.rs中的代码,该文件内容为:
Filename: src/garden.rs
pub mod vegetables;
这里 pub mod vegetables; 表示同时包含了src/garden/vegetables.rs中的内容,该文件内容为:
#[derive(Debug)]
pub struct Asparagus {}
接下来我们深入了解这些规则并演示其实践!
将相关代码归类到各个模组中
Modules 可以帮助我们组织crate内部的代码,提高可读性和复用性。同时,它们还能控制项目成员间隐私,因为默认情况下,一个module内部定义的是私有项,不对外暴露。这些“私有”元素仅作为实现细节存在,不供外部调用。如果需要,也可以选择把整个module或其中特定元素设为public,从而允许外部依赖调用它们。
举例来说,我们来写一个餐厅功能库crate,仅定义函数签名不实现具体逻辑,以便专注于如何组织这段程序,而不是餐厅业务本身。
餐饮行业常把餐厅划分成“前台”和“后台”。前台指客户所在区域,包括迎宾安排座位、服务员点单付款以及调酒师调饮料等环节;后台则是厨师做菜、洗碗工清洁及管理人员处理行政事务之地。
为了按此思路构建我们的crate,可采用嵌套modules来分类函数。在终端运行 cargo new restaurant --lib 新建库,然后编辑src/lib.rs,将7-1列表所示源码输入进去,实现前台(front_of_house)部分功能签名:
Filename: src/lib.rs
mod front_of_house {mod hosting {fn add_to_waitlist() {}fn seat_at_table() {}}mod serving {fn take_order() {}fn serve_order() {}fn take_payment() {}}
}
7-1列表:“front_of_house” module 包含两个子module,每个都含若干函数
我们用mod关键词加名称定义module(此处即front_of_house),然后把主体放入花括号{}内。在这个module里,还能继续嵌套其它modules,如hosting与serving。此外modules还能容纳structs, enums, constants, traits等各种定义,本例展示的是functions。
利用modules,我们能够聚合相关定义,并赋予其意义上的关联名称。这样程序员阅读时就能基于group定位目标,而不用遍历所有定义,更易找到关心部分;新增功能时也知道应往哪儿添加才能保持结构整齐。
此前提过,src/main.rs 与src/lib.r s被称作crateroots,是因为这两个源文件里的全部内容组成了顶层叫做crate名字空间(module)树状结构(root of the module tree)。
7-2列表显示了7-1列出的结构对应得module树形图:
crate
└── front_of_house ├── hosting │ ├── add_to_waitlist │ └── seat_at_table └── serving ├── take_order ├── serve_order └── take_payment
7-2列表:“Listing 7–1”的Module树形图
这棵树展现了一些modules嵌套关系,比如hosting就是nest(嵌套) 在front_of_house里面。同时也体现出兄弟关系——hosting与serving都是front_of_house下平级兄弟节点。如果A module包含B module,则说B是A孩子节点(A parent)。注意整个tree由隐式root叫做crate起始。
这个module树很像电脑中文件系统目录树,非常贴切!正如filesystem里的目录用于整理存储,同理你借助modules整理你的code。而且跟directory里的files一样,你需要方法去定位自己的modules。
上面这段话太难理解,请你以图文形式展示。但是不要改变文章的意思。但你需要完整的返回文章内容