网上说,Rust 的学习曲线不像其他语言那样,用一句话描述就是:从入门到入门。这用在我身上很准确,我先后曾不止两次入门,每两次之间又都相隔数月,以至于经常需要翻阅 Rust 官方书 《Rust 编程语言》。
不过说真的,随着自学的逐步加深,会觉得越来越喜欢 Rust(可能是没有在实际项目中深入的使用导致),但既然很喜欢,那就借着这份热情,好好地了解“她”。
从去年到现在,差不多一年左右,除了把官方书看完了,还利用碎片时间将范长春前辈的《深入浅出 Rust》看完。大部分是在地铁上看的,所以实践的不够,这篇记录 Rust mod 也是其中一篇笔记。
在阅读该篇笔记之前,需要你先认真读完官方书的 mod 章节
由于 Rust 提供了非常好用的工具链 —— cargo,用户只需简单的 cargo new pro_name 即可创建一个目录很规范的可执行程序的项目,用户做的只需将精力放在开发逻辑上,大大减少了心智负担。而这也能满足很多类型的中小项目。
而在编写大型的项目时,又可以通过 Rust 的模块化支持来很好的实现。通过 Rust 模块的学习,我们可以看出,Rust 模块很强大并且不失灵活。例如:
1.你可以在单个文件中编写多个模块。
// src/notes/test_module/single_mod.rs
pub mod single_mod {
pub mod my_mod1 {
pub fn mod1_func1() {
println!("file:single_mod-single_mod-my_mod1-mod1_func1");
}
}
pub fn func_0() {
// 调用父级模块下的函数
println!("调用父级模块下的函数:");
super::level1_mod1::mod1_func1();
// 调用同级下的模块下的函数
println!("调用同级下的模块下的函数:");
my_mod1::mod1_func1();
println!("file:single_mod-single_mod-func_0");
}
}
pub mod level1_mod1 {
pub fn mod1_func1() {
println!("file:single_mod-level1_mod1-mod1_func1");
}
}
2.也可以将一个 rs 文件作为一个模块。将一个文件作为一个模块,也就意味着里面可能会有函数声明、类型声明、trait 声明等。
// 函数声明
pub fn one_file_func() {
println!("file:one_file-one_file_func");
}
trait Human {
fn speak();
}
// 类型声明
#[derive(Debug)]
pub struct Stu {
id: i32,
name: String,
age: u8,
}
impl Human for Stu {
fn speak() {
println!("I speak Chinese.")
}
}
impl Stu {
pub fn new() -> Stu {
Stu {
id: 1,
name: String::from("张太一"),
age: 24,
}
}
}
3.在一个 crate 中你可以做到多个模块、子模块的嵌套。
// src/notes/test_module/single_mod.rs
pub mod single_mod {
pub mod my_mod1 {
pub fn mod1_func1() {
println!("file:single_mod-single_mod-my_mod1-mod1_func1");
}
}
pub fn func_0() {
// 调用父级模块下的函数
println!("调用父级模块下的函数:");
super::level1_mod1::mod1_func1();
// 调用同级下的模块下的函数
println!("调用同级下的模块下的函数:");
my_mod1::mod1_func1();
println!("file:single_mod-single_mod-func_0");
}
}
单文件多模块
先说单个 rs 文件中的模块。在一个 rs 文件中,我们可以通过 mod 关键字新建很多个模块,例如 single_mod.rs 中的示例代码。
这种比较适合小型的工具类的程序,不需要太多文件,就能在有限的文件中,拆分 module。局限性就是不适合代码多的项目,拆分不是很清晰。
单仓库多模块
这里的单仓库,本意是指在单个的 crate 中。前两天刚好看到了一个文章 —— Rust module 系统详解,这篇文章由浅入深的阐释了 crate 和 module 的联系。
在一个 crate 中,我们可以声明很多个 module。这里我们以一个 Rust bin 程序作为示例,用 cargo new --bin crate_module 创建一个项目框架,使用 tree . 可查看其目录如下所示:
.
├── Cargo.toml
└── src
└── main.rs
1 directory, 2 files
这个 crate 的根模块可以认为是从 src 目录开始,当我们要在 main.rs 中调用 user_service.rs 中的方法/函数时,需要先在 main.rs 文件中引入 —— mod services; ,使用了这个语句后,Rust module 系统会有两个选择:
- 1.在当前目录下寻找 services.rs 文件。因为一个 rs 文件可以看做一个 module
- 2.在当前目录下寻找 services/mod.rs 文件。此时 services 可以看做是一个命名空间,它背后,可以有很多的 module(通过 mod.rs 文件管理)
而在我们的“crate_module”例子中,恰好是第 2 种方式 —— 加载 services/mod.rs 文件。
增加了 services 目录后的文件列表如下:
.
├── Cargo.lock
├── Cargo.toml
├── src
│ ├── main.rs
│ └── services
│ ├── mod.rs
│ └── user_service.rs
因此,在项目的 src 目录下,main.rs 中如果要引入一个 helper.rs 文件,我们只需在 main.rs 文件中这样做:mod helper; ,这样在 main.rs 中就能使用 helper.rs 文件中导出的类型、函数等内容。
mod helper;
use helper::Helper;
fn main() {
let h1 = Helper::new();
println!("{:?}", h1);
}
workspace
上面一节所说的将许多的 module 分散在不同的目录、文件中,可以将项目拆分开,以写出更加抽象、可复用的代码。
当随着项目开发的深入,业务的增长,代码越来越多,就会导致 crate 持续增大,我们可能会希望将其拆分成多个 crate,这有些类似于 web 开发中的微服务,将服务抽象进行抽象,拆分成不同的模块和类型,通过接口来进行调用。Rust 中 workspace 的概念就是类似的作用。
在官方的 cargo 书上,对 workspace 也有比较详细的介绍
更加详细的 workspace 实践,可以参考这里的实例。
其他
reference
|
请发表评论