首頁 > 軟體

深入講解下Rust模組使用方式

2022-03-05 13:00:35

前言

本文適用於剛開始學習rust的同學,用於幫助理解rust模組間是如何相互參照的。本文儘量用極少的程式碼來演示,即便之前沒有了解過rust也可以混個眼熟。用的時候可以有個印象。

如果你之前沒有了解過rust,只需要知道:Cargo-依賴管理工具,類似於npm,Cargo 使用檔案放置約定,即檔名就是模組名。crate-集裝箱,是一組功能的封裝,類似於npm包。

本文探討的場景是在專案中對程式碼進行不同程度的檔案拆分和模組抽離時,往往需要在一個檔案中引入另一個模組的部分程式碼,在javascript中,我們可以通過匯入匯出來使用其他模組的程式碼,這個過程我們只需要關心匯入路徑是否正確。

export const name= xx 
import lodash from './lodash'

而在rust中,模組不再通過檔案路徑的方式引入,而是通過cargo以及約定的模組宣告方式來構建模組樹,然後通過use關鍵字來使用。但是rust的檔案在檔案拆分和模組使用上做的範例不太詳細,於是就參考一些釋出的crate的組織方式進行了梳理。

模組宣告&使用

假如我們想實現一個加法模組,並提供給其他地方使用。我們可以有如下三種組織方式

Cargo 使用檔案放置約定,因此模組查詢以src目錄下的rs檔案或者目錄為準,並且只會查詢一級,巢狀資料夾下的rs檔案不可以直接被其他檔案使用。

方法一:直接在根檔案下宣告 add.rs

我們可以通過在src下新增模組名同名的檔案,cargo就可以識別到add模組。

├── Cargo.lock
├── Cargo.toml
├── src
│   ├── add.rs
│   ├── lib.rs

方法二:宣告add資料夾,資料夾下包含 mod.rs

如果模組是資料夾,則必須有mod.rs檔案。這類似於javascript的index.js。cargo仍然可以識別到這是add模組

├── Cargo.lock
├── Cargo.toml
├── src
│   ├── add
│   │   ├── mod.rs
│   ├── lib.rs

假設我們的程式碼內容如下,並位於檔案add.rs 或者add/mod.rs內

pub fn add_fn(a: i32, b: i32) -> i32 {
    a + b
}

那麼在lib.rs中我們可以通過如下方式呼叫我們的add模組

// 宣告模組並參照模組內的函數
mod add;
pub use crate::add::add_fn;
pub fn test_lib() {
  add_fn(1,2);
}

方法三:add.rs和add資料夾同時存在

這種方式的目錄結構看起來像下面這樣

├── Cargo.lock
├── Cargo.toml
├── src
│   ├── add
│   │   └── add_m.rs
│   ├── add.rs // index.js
│   ├── lib.rs

add.rs負責入口模組的匯入匯出,add資料夾下則存放其餘相關聯的其他模組。這類似於javascript的index.js統一匯出了多個其他模組。和上面不同的是這裡 匯入使用到了mod關鍵字來拆分模組;

檔案內容看起來像下面這樣

add.rs

pub mod add_m;
// 類似於 export * from './validate; export * from './helper'

add/add_m.rs

pub fn add_fn(a: i32, b: i32) -> i32 {
    a + b
}

lib.rs

mod add;
pub use crate::add::add_m::add_fn;
pub fn test_lib() {
  add_fn(1,2);
}

上述三種方式使用較多的應該是前兩種,並且在大型專案內第二種更為合理,可以更好的組織檔案。那麼當一個模組資料夾下拆分多個模組檔案時該怎呼叫相鄰檔案呢?

同模組相鄰檔案參照

我們調整目錄結構如下

├── Cargo.lock
├── Cargo.toml
├── src
│   ├── add
│   │   ├── mod.rs
│   │   ├── print.rs
│   │   └── user.rs // user會呼叫print的方法
│   ├── lib.rs

在add模組下多了print和user。user會呼叫print的方法。

print.rs

pub mod my_print {
    pub fn print_hello() {
        println!( hello )
    }
}
// 這裡的pub mod 可以簡單理解為ts的declare module ,裡面是module的可用屬性
// declare module my_print {
//  export function print_hello(): string;
// }

user.rs

use super::print::my_print;
pub fn hello_user() {
    my_print::print_hello();
}
pub struct User {
    pub name: String,
}

同模組下的檔案互相參照使用super關鍵字。

mod.rs

// mod.rs為入口檔案, 下面用mod宣告會先去同資料夾下查詢同名檔案,如果沒有則看是否有滿足條件   的同名資料夾
// 例如 add 資料夾下沒有print.rs 則查詢是否有print資料夾並且資料夾下有mod.rs。
mod print;
mod user;

// 因為是同一個模組資料夾下,並且在入口檔案使用,所以這裡應self
pub use self::user::hello_user;
pub use self::user::User;

pub mod add_fn {
    // use super::*; 如果有這行,則下面不用每次呼叫super
    pub fn add(a: i32, b: i32) -> i32 {
      // 注意這裡super關鍵字,因為hello_user是在另一個模組宣告的,模組間不能直接呼叫所以需要使用super來從模組根進行查詢呼叫
        super::hello_user();

        let value = super::User {
            name: String::from( Rust ),
        };

        println!( user name {} , value.name);
        a + b
    }
}

pub fn test_out_ref() {
  // 這裡不在需要super因為不在mod內定義
    hello_user();
}

不同模組參照

我們新增一個模組multip,返回兩個數相乘的結果,目錄結構如下

├── Cargo.lock
├── Cargo.toml
├── src
│   ├── add
│   │   ├── mod.rs
│   │   ├── print.rs
│   │   └── user.rs // user會呼叫print的方法
│   ├── multip  // ------- 新增這個模組
│   │   ├── mod.rs法
│   ├── lib.rs

multip/mod.rs

pub fn res_multip(a: i32, b: i32) -> i32 {
    a * b
}

假設add檔案引入multip

mod print;
mod user;

pub use self::user::hello_user;
pub use self::user::User;
// 新增下面這行
use crate::multi::multip;

如此便可以使用另一個模組的內容了。當然其他模組的相互參照方式一致。

小結

rust的模組使用方式總體來說是比較簡單的,由於官方檔案在模組拆分和組織上並沒有進行較完善的說明,所以對於剛從js轉到rust學習的同學可能會有一點不適應。通過前面內容已經較為清晰的梳理了下使用方式。希望可以對需要的同學有所幫助。

到此這篇關於Rust模組使用方式的文章就介紹到這了,更多相關Rust模組使用方式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com