1. 程式人生 > 實用技巧 >Rust 學習之 mod

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