简介
- 接上回
Rust 的模块系统的基本概念
Package、Crate、Module、Path:
顶层、Package 包,Cargo的特性,让你构建、测试、共享crate
第二层、Crate 单元包,一个模块数,它可以产生一个library 或可执行文件。
第三层、Module 模块,控制代码的组织、作用域、私有路径。
最后、Path 路径,为struct、function或module等项命名的方式。
Crate 相关
- 包(package)创建规则:
1、一个包中至多只能包含一个库Crate。
2、包中可以包含任意多个二进制Crate。
3、包中至少包含一个 crate,无论是库的还是二进制的。
4、包中应该包含一个 Cargo.toml 配置文件,用来说明如何去构建这些 crate。
- Package 的特点:
1、包含一个 Cargo.toml
2、默认的项目的 `binary crate` 的入口文件是 src/main.rs
3、默认的库 `library crate` 的入口文件是 src/lib.rs 库名称与Package的名称相同。
4、所以一个package 既可以包含一个 `main.rs` 也可以包含 一个 `lib.rs`
5、文件放置 src/bin 下面,每个文件都是单独的binary crate
s
定义Module 来控制作用域和私有性
- Module 在一个crate内,将代码进行分组。
- 增加可读性,易于复用。
- 控制项目(item)的私有性,通过public、private
- 通过 mod 关键字创建module
- module 是可嵌套的。
- module 可以包含其他项(struct、enum、常量、trait、函数等)的定义
代码测试
- 举一个例子比上面的文字更有意义:
pub mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist () -> u32 {
println!("############ add_to_waitlist ############");
555
}
}
mod serving {
pub fn take_order (){
println!("RUN take_order");
}
pub fn serve_order (){
println!("RUN serve_order");
}
}
}
路径 Path
- 为了在Rust 的模块中找到某个条目,需要使用路径。
- 路径的两种形式:
1、绝对路径,从 crate root 开始,使用crate 名或者字面值crate
2、相对路径,从当前模块开始,使用self,super 或者当前模块的标识符号。
- 路径至少由一个标识符组成,标识符之间使用
::
使用pub 关键字
- 默认上 Rust 中所有条目(函数,方法,struct,enum,模块,常量)默认是私有的
- 父级模块无法访问子模块中的私有条目。
- 子模块里可以使用所有祖先模块中的条目。
- pub 放在 struct 前,struct 是公共的,但是struct 的字段默认是私有的。
- pub 放在 enum 前,enum 是公共的,里面枚举变体也都是公用的,这个比较特殊,要注意。
super 关键字
-
super
用来访问父级模块路径中的内容,类似文件系统中的..
use 关键字
- use 是作用域引入关键字。
- use 同样需要遵循私有性原则,实例代码如下:
fn main(){
// 通过引用可以让代码更精炼。
use hello::front_of_house::hosting;
let num = hosting::add_to_waitlist();
println!("AAA{}", num);
}
as 关键字
- 别名关键字,举例
use hello::front_of_house::hosting as newhost;
fn main(){
// 通过引用可以让代码更精炼。
let num = newhost::add_to_waitlist();
println!("AAA{}", num);
}
打包引用多个模块
- 如果模块重复可以通过 { } 括号区块进行打包
use std::io;
use std::io::Write;
// 可以改成
use std::io:: { self, Write} ;
pub use 重新导出名称
- 使用 use 将路径导入到作用域内后,该名称在此作用域内是私有的。
- pub use 重导入可以将条目引入作用域,该条目就可以被外部代码引入到他们的作用域。
通配符 * 引入
- 通配符 * 可以把路径中所有公共条目都引入到作用域中,但是要谨慎使用,一般在测试的时候才需要,或者被用于预导入时。
使用外部包
- 在Cargo.toml 中添加依赖的包,之后运行 cargo build 时候程序会 crates.io 寻找依赖库。
- 举例,修改 Cargo.toml
[package]
name = "hello"
version = "0.1.0"
authors = ["Kami1983 <meetcancanyou@yahoo.com>"]
edition = "2018"
# 添加依赖
[dependencies]
rand = "0.8.3"
- 外部包的使用同样需要use 关键字,例如:
use rand::Rng;
fn main() {
// 生成一个0~9的的随机数,gen_range 根据Rust 版本不同参数写法上也是有区别的需要注意
// 较新版本的是 gen_range(0,10)
let randnum = rand::thread_rng().gen_range(0..10);
println!("Rand num is : {}", randnum);
}
如何将模块拆分到不同的文件
- 拆分是降解负责度最好的办法没有之一,所以Rust 也可以将模块内容拆分到不同的文件中的,而且分厂简单。
- Rust 会从与模块同名的文件中加载内容,但却可以让模块树的结构不发生变化。
- 随着模块逐渐变大,该技术可以让你把模块的内容移动到其他文件中。
- 举例,之前有一段代码如下:
mod testmod {
pub fn say_words() {
println!("Hello world!");
}
}
fn main() {
testmod::say_words();
}
- 突然间觉得代码过于臃肿,要将 testmod 单独放到其他的文件中就可以简单的改进一下:
# 原来的 main.rs
// 直接 mod 名字; 就可以将该模块分离,成 testmod.rs 文件
mod testmod ;
fn main() {
testmod::say_words();
}
------------------ 注意这里面的内容放到 testmod.rs 单独文件中
# 分离出来的 testmod.rs
pub fn say_words() {
println!("Hello world!");
}
- 运行之后效果相同
结束
- 感谢阅读,这章节还是很有意思的,因为一个好的结构化代码,离不开项目工程结构的优化和解耦。