模組系統(Modules)#

模組(Module) 讓你將程式碼分組、控制可見性,並組織大型專案的結構。 Rust 的模組系統由三層構成: package(套件)crate(包)module(模組)


1. mod — 定義模組#

mod 關鍵字建立模組,可以巢狀:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
mod greetings {
    pub fn hello() {
        println!("你好!");
    }

    pub fn goodbye() {
        println!("再見!");
    }
}

fn main() {
    greetings::hello();   // 你好!
    greetings::goodbye(); // 再見!
}

2. pub — 控制可見性#

模組內所有項目預設是 私有的 ,只有加上 pub 才能從外部存取:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mod auth {
    pub struct User {
        pub username: String,
        password: String, // 私有欄位,外部無法直接存取
    }

    impl User {
        pub fn new(username: &str, password: &str) -> User {
            User {
                username: String::from(username),
                password: String::from(password),
            }
        }

        pub fn is_valid(&self, password: &str) -> bool {
            self.password == password // 私有欄位只能在模組內使用
        }
    }
}

fn main() {
    let user = auth::User::new("alice", "secret123");
    println!("用戶名:{}", user.username);              // OK,公開欄位
    // println!("{}", user.password);                   // 錯誤!私有欄位
    println!("驗證:{}", user.is_valid("secret123"));   // OK
}

super 與 self#

在模組內可以用 super 存取上一層,self 指向目前模組:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
mod outer {
    pub fn greet() {
        println!("外層");
    }

    pub mod inner {
        pub fn greet() {
            super::greet(); // 呼叫上一層的 greet
            println!("內層");
        }
    }
}

fn main() {
    outer::inner::greet();
}

3. use — 引入路徑#

每次都寫完整路徑很繁瑣,用 use 引入:

1
2
3
4
5
6
7
8
9
mod greetings {
    pub fn hello() { println!("你好!"); }
}

use greetings::hello;

fn main() {
    hello(); // 不需要寫 greetings::hello()
}

引入多個項目#

1
use std::collections::{HashMap, HashSet};

重新命名(as)#

1
2
3
4
5
6
use std::collections::HashMap as Map;

fn main() {
    let mut m: Map<&str, i32> = Map::new();
    m.insert("a", 1);
}

pub use — 重新匯出#

讓外部使用者不必知道內部模組結構:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
mod shapes {
    pub mod circle {
        pub fn area(r: f64) -> f64 {
            std::f64::consts::PI * r * r
        }
    }
}

// 將 circle::area 重新匯出,外部可直接用 shapes::area
pub use shapes::circle::area;

4. 多檔案模組#

實際專案中,模組通常分散在不同檔案。

目錄結構:

src/
├── main.rs
├── greetings.rs           ← greetings 模組
└── math/
    ├── mod.rs             ← math 模組入口
    └── geometry.rs        ← math::geometry 子模組

src/greetings.rs

1
2
3
pub fn hello(name: &str) {
    println!("你好,{name}!");
}

src/math/mod.rs

1
2
3
4
5
pub mod geometry;

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

src/math/geometry.rs

1
2
3
pub fn circle_area(r: f64) -> f64 {
    std::f64::consts::PI * r * r
}

src/main.rs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
mod greetings;
mod math;

use greetings::hello;
use math::geometry::circle_area;

fn main() {
    hello("Alice");
    println!("圓面積:{:.2}", circle_area(3.0));
    println!("加法:{}", math::add(1, 2));
}

現代 Rust(2018 Edition 後)也支援用同名目錄取代 mod.rs,例如將 math/mod.rs 改為 math.rs,效果相同。


5. Cargo 與套件管理#

Cargo.toml#

每個 Rust 專案由 Cargo.toml 管理。加入外部套件(crate):

1
2
3
[dependencies]
serde = { version = "1", features = ["derive"] }
rand = "0.8"

執行 cargo add rand 可自動新增,然後 cargo build 下載並編譯。

使用外部套件範例#

1
2
3
# Cargo.toml
[dependencies]
rand = "0.8"
1
2
3
4
5
6
7
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let n: u32 = rng.gen_range(1..=100);
    println!("隨機數:{n}");
}

常用外部套件#

套件用途
serdeJSON / YAML 序列化與反序列化
tokioasync 非同步執行環境
reqwestHTTP 客戶端
clap命令列參數解析
anyhow簡化錯誤處理
rand隨機數產生
regex正規表達式

6. Crate 類型#

一個 Rust 套件(package)可以包含:

檔案類型說明
src/main.rsbinary crate可執行程式的入口
src/lib.rslibrary crate函式庫,供其他程式引用
src/bin/*.rsbinary crate多個可執行程式

若要建立函式庫讓他人使用:

1
cargo new my_lib --lib

src/lib.rs 中定義公開介面,其他程式即可在 Cargo.toml 加入依賴後使用。


Reference#

https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html