包、单元包、模块
Rust的模块系统包括
- 包(package):一个用于构架、测试并分享单元包的功能
- 单元包(crate):一个用于生成库或可执行文件的树形模块结构
- 模块(module)和use关键字:控制文件结构,作用域及路径的私有性
- 单元包(crate):一个用于生成库或可执行文件的树形模块结构
- 路径(path):一种用于命名条目(结构体、函数、模块)的方法
包与单元包
crate类型
- 二进制(binary)
- 库(library)
crate 作用
- 将相关的功能组合到一个作用域内,便于在项目间进行共享
crate root
- 是源代码文件
- Rust编译器从这里开始,组成Crate的根Module
- Cargo 把 crate root 文件交给 rustc 来构建 library 或 binary
一个package
- 包含一个Cargo.toml,描述了如何构建这些Crates
- 至少有一个crate
- 最多包含一个library crate
- 可以包含任意数量的library crate
使用 cargo new
创建出来package
一些惯例:
-
在某个package下
src/main.rs
- binary crate 的 crate root
- crate名与 package名相同
src/lib.rs
- 代表该package包含一个library crate
- library crate 的 crate root
- crate名与 package名相同
-
一个 package 可以有多个 binary crate
- 文件放在
src/bin
里面 - 每个文件是单独的binary crate
- 文件放在
通过定义模块(module)来控制作用域及私有性
Module
- 在一个crate内,将代码进行分组
- 增加可读性。易于复用
- 控制项目的私有性
建立Module
- mod 关键字:
mod 模块名 {...}
- 可嵌套
- module内可包含其他项(结构体、枚举、常量、trait、函数等)的定义
例子:在package my_project下的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() {}
}
}
src/main.rs
和src/lib.rs
称为crate root就体现在这,这两个文件的内容各自组成了名为crate的模块,位于crate模块结构的根部- 这个模块结构也称为模块树。该例子展示的模块树是下面这样的
将模块拆分为不同文件
可以将模块的定义放在和模块名同名的文件中。
注意,模块的文件树结构要和上面的模块树结构完全一致。比如将上面的模块完全拆分,文件结构是
src/lib.rs
:
mod front_of_house ;
src/front_of_house.rs
:
mod hosting ;
mod serving ;
src/front_of_house/hosting.rs
:
fn add_to_waitlist () {}
fn seat_at_table() {}
用于在模块树中指明条目的路径
与文件系统非常相似
路径有两种格式
- 使用单元包的名称或字面量
crate
从根节点开始的绝对路径 - 使用
self
,super
,或当前模块名来从当前模块开始的相对路径 - 多个标识符之间用
::
分割
使用例子:下面这个例子会报错,原因马上就会解释
mod front_of_house {
mod hosting {
fn add_to_waitlist () {}
}
}
pub fn eat_at_restaurant() {
//绝对路径
crate::front_of_house::hosting::add_to_waitlist();
//相对路径
front_of_house::hosting::add_to_waitlist();
}
基于实际情况进行决策,通常使用绝对还是相对路径取决于你是否会同时移动 条目的定义代码和使用该条目的代码
- 是,基本上使用相对路径方便
- 不是,使用绝对路径
使用super关键字构造相对路径
像在文件系统中使用../
一样,表示当前位置的父级节点
fn serve_order() {}
mod back_of_house {
fn fix_order() {
cook_order();
super::serve_order();
}
fn cook_order() {}
}
使用pub关键字来暴露路径
私有边界
上面的代码无法通过编译,是因为模块定义了Rust中的私有边界
- Rust中的所有条目(函数、方法、结构体、枚举、模块、常量)默认都是私有的
- 处于父级模块的条目无法使用子级模块的私有条目,子级模块的条目可以使用所有祖先模块中的条目
如何修改上面的代码?
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist () {}
}
}
- 第一个pub使得我们在访问模块front_of_house时能访问其子模块hosting
- 第二个pub使得我们在访问模块hosting时能访问里面的函数add_to_waitlist
将结构体或枚举声明为pub
结构体
- 在结构体定义前加pub,结构体本身成为公共的,但是其字段依旧保持私有状态
- 我们可以在字段前加pub来逐一决定某字段是否公开
mod back_of_house{
pub struct Breakfast {
pub toast : String,
seasonal_fruit : Srting,
}
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");
meal.toast = String::from("Wheal"); //合法
//接下来不合法
//meal.seasonal_fruit = String::from("berries");
}
一点细节:由于结构体有私有的字段,所以提供一个共有的构造函数来进行初始化(在C++中我们太熟悉这一套了)
枚举
与结构体不同,将一个枚举声明为pub时,连带其所有字段也都是pub的了
mod back_of_house{
pub enum Appetizer {
Soup,
Salad,
}
}
pub fn eat_at_restaurant() {
let order1 = back_of_house::Appetizer::Salad;
}
使用 use 关键字将路径导入作用域
使用use将路径导入作用域,并且像使用本地条目一般调用路径中的条目
mod front_of_house {
mod hosting {
fn add_to_waitlist () {}
}
}
use crate::front_of_house::hosting;
pub fn eat() {
hosting::add_to_waitlist();
}
通过use crate::front_of_house::hosting;
,hosting
成为该作用域下的有效名称
上面展示的是绝对路径,而使用相对路径时,对于从当前节点起始的符号串,必须以self
开始
mod front_of_house {
mod hosting {
fn add_to_waitlist () {}
}
}
use self::hosting;
pub fn eat() {
hosting::add_to_waitlist();
}
创建use路径时的惯用模式
- 对于函数而言,通常引入到函数的父模块。之后调用函数时,通过
模块名::函数名
避免重名函数的风险和更好的阅读性 - 对于结构体和枚举,直接引入完整路径。比如
use std::collections::HashMap;
使用as关键字提供新名称
为了防止重名,可以用 use a as b
来为引入的条目重命名(像是python的 import as 那样)
use std::fmt::Result;
use std::io::Result as IoResult;
使用pub use 重导出名称
- 使用普通的
use ...
导入时,该名称以私有的方式在新的作用域中生效 - 使用
pub use ...
导入后,外部代码也能访问到该名称,甚至可以被外部代码从这个新的作用域引入
使用嵌套的路径来整理use语句
使用这样的语法路径相同部分::{不同部分,不同部分,...}
比如合并
use std::cmp::Ordering;
use std::io;
为
use std::{io,cmp::Ordering};
特殊情况是,使用self
来代表“当前节点”
合并
use std::io;
use std::io::Write;
为
use std::io::{self,Write};
通配符*
将定义在某个路径中的所有公共条目都导入作用域,在指定路径时最后使用*
use std::collections::*;