Module
在编写较为复杂的项目时,合理地对代码进行组织与管理很重要,rust 提供了一系列的功能来帮助我们管理代码,包括决定哪些细节是暴露的、哪些细节是私有的,以及不同的作用域内存在哪些名称。
通过定义模块来控制作用域及私有性
在 src 目录下创建 lib.rs 文件,使用 mod 关键字加大括号定义一个模块,可以多层嵌套:
// src/lib.rsmod front_of_house { // 模块中的内容默认都是隐藏的,需要使用pub将其公开 pub mod hosting { pub fn add_to_waiting() {} fn seat_to_table() {} } mod serving { fn take_order() {} fn serve_order() { fn take_payment() {} }}
fn eat_at_restaurant() { // 引用模块内容 // 绝对路径使用crate开头,因为front_of_house是根模块, // 可以理解为绝对路径: /front_of_house crate::front_of_house::hosting::add_to_wait_list();
// 使用相对路径,因为front_of_house和 // eat_at_restaurant在同一级,可以理解为:./front_of_house front_of_house::hosting::add_to_wait_list()}
复制代码
super 关键字
每一个模块是单独的作用域,而且作用域并不会自动向上查找,查找当前模块的上级作用域,可以使用 super:
// back_of_house模块的作用域外部fn serve_order() {}
mod back_of_house { fn cook_order() {}
fn fix_incorrent_order() { // 当前模块作用域中可以直接使用 cook_order();
// 获取父级模块的内容,可是使用super关键字开头,可以理解为:../ super::serve_order() }}
复制代码
公开的结构体
当我们在结构体定义前使用 pub 时,结构体本身就成为了公共结构体,但它的字段依旧保持了私有状态。我们可以逐一决定是否将某个字段公开:
mod back_of_house { pub struct Breakfast { // 只公开toast字段 pub toast: String, // 这个字段仍然是私有的 seasonal_fruit: String, }
impl Breakfast { pub fn summer(toast: &str) -> Breakfast { Breakfast { toast: String::from(toast), seasonal_fruit: String::from("peaches"), } } }}
pub fn eat_at_restaurant() { let mut meal = back_of_house::Breakfast::summer("Rye");
// 可以访问更改toast属性 meal.toast = String::from("Wheat"); println!("I'd like {} toast please", meal.toast); // I'd like Wheat toast please
// 无法编译通过,因为seasonal_fruit不是公开属性 meal.seasonal_fruit = String::from("blueberries");}eat_at_restaurant();
复制代码
公开的枚举
当我们将一个枚举声明为公共的时,它所有的变体都自动变为了公共状态。我们仅需要在 enum 关键字前放置 pub:
mod back_of_house { // 对枚举添加pub,会将所有的枚举值公开 pub enum Appetizer { Soup, Salad, }}
pub fn eat_at_restaurant() { let order1 = back_of_house::Appetizer::Soup; let order2 = back_of_house::Appetizer::Salad;}
复制代码
使用 use 关键字引用模块
use 可以引用当前模块中的内容,支持绝对路径和相对路径:
mod front_of_house { pub mod hosting { pub fn add_to_wait_list() {} }}
// 绝对路径方式use crate::front_of_house::hosting; // 方式1use crate::front_of_house::hosting::add_to_wait_list; // 方式2
// 或者相对路径use self::front_of_house2::hosting; // 方式1use self::front_of_house2::hosting::add_to_wait_list; // 方式2
fn eat_at_restaurant() { hosting::add_to_wait_list() // 方式1使用 add_to_wait_list() // 方式2使用}
复制代码
使用 as 关键字对引用模块重命名
当我们引用相同名称的内容会引起冲突,这是需要使用 as 关键字对其中一项进行重命名:
use std::fmt::Result;use std::io::Result as IoResult;
复制代码
重新导出(reexporting)
我们对用户暴露的内容通常需要暴露在一个出口中,这样方便用户导入,
我们需要先将其他的引用导入到同一个模块中,然后重新导出:
pub use crate::front_of_house2::hosting;pub use std::fmt::Result;
复制代码
外部模块
当我们使用第三方模块时,需要先在 cargo.toml 中添加依赖,例如使用一个能够获取随机数的包:
[dependencies]rand = "0.5.5"// 运行 cargo build
复制代码
引入 rand:
use rand::Rng;fn main() { let secret_number = rand::thread_rng().gen_range(1, 101);}
复制代码
引入标准库中的模块,标准库虽然不用在 toml 中声明,也不用下载,但是也需要显式引入:
use std::collections::HashMap;
复制代码
嵌套批量导入
use std::{cmp::Ordering, io};// 等同于use std::cmp::Ordering;use std::io;
复制代码
使用 self 表示引用导入路径的自身:
use std::io::{self, Write};// 僧同于use std::io;use std::io::Write;
复制代码
使用*运算符导入所有
// 引入collections下面所有的内容use std::collections::*// 直接使用引入的内容BTreeMap::new();
复制代码
将模块拆分到不同的文件
上边的所有例子都是在同一个文件中,我们可以把不同功能的代码拆分到不同的文件中维护,有两种方式可以实现,例如我们要在 lib.rs 中使用如下结构的模块:
// src/lib.rsmod a; // 引入a模块use a::structs::{ A1, A2 }; // 使用a模块中的结构体中的A1,A2结构体
复制代码
方式一
使用模块同名文件夹结构来组织,这种组织方式将一个模块的同名文件夹作为模块的内容,模块本身作为入口文件,首先创建 a.rs 文件作为 a 模块的入口文件:
创建 a 模块的同名文件夹,用于放置 a 模块的内容:
在文件夹 a 中创建结构体模块:
创建 A1 和 A2 结构体:
// src/a/structs.rspub struct A1 {}pub struct A2 {}
复制代码
最后在 a 模块的入口文件中引入 structs 模块并向外导出:
// src/a.rspub mod structs;
复制代码
最后结构如下:
src├── a│ └── structs.rs├── a.rs├── lib.rs
复制代码
方式二
使用模块入口放在模块文件夹中的方式来组织,这种方式将文件夹作为模块名称,在文件夹中使用 mod.rs 作为模块入口,首先创建模块文件夹:
在文件夹 a 中创建结构体模块:
创建 A1 和 A2 结构体:
// src/a/structs.rspub struct A1 {}pub struct A2 {}
复制代码
本次不再创建 src/a.rs 文件,而是在文件夹 a 中放置入口文件 mod,名称是固定的,就好像我们在 JS 中一般使用 index.js 如出一辙:
在入口文件中引入并导出 a 模块的内容:
// src/a/mod.rspub mod structs;
复制代码
这种方式的目录接否如下:
src├── a│ ├── mod.rs│ └── structs.rs├── lib.rs
复制代码
本章节只介绍了模块的基本用法和两种目录结构的使用方式,更详细的内容可以自行去翻阅原书。
评论