Rust 学习之 mod

Rust 学习之 mod

网上说,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

posted @ 2020-08-21 10:09  suhanyujie  阅读(3709)  评论(0编辑  收藏  举报