模組與 Crate
程式變大之後,需要把程式碼分門別類,並控制哪些部分對外公開。Rust 用「套件、crate、模組、路徑」這套系統來組織程式。本頁說明各名詞的關係,以及最常用的 mod、pub、use 語法。
名詞對照
| 名詞 | 說明 |
|---|---|
| 套件(package) | 一個 Cargo 專案,由 Cargo.toml 描述,可包含一個或多個 crate |
| crate | 編譯的最小單位,分成執行檔(binary,入口 src/main.rs)與函式庫(library,入口 src/lib.rs) |
| 模組(module) | crate 內部的命名空間,用來分組與控制可見性 |
| 路徑(path) | 指到某個項目的名稱,例如 crate::garden::plant |
定義模組與可見性
用 mod 定義模組。預設所有項目都是私有的,要讓外部使用必須加上 pub:
mod restaurant {
pub mod kitchen {
pub fn cook(dish: &str) -> String {
format!("煮好了:{dish}")
}
fn secret_recipe() {} // 私有,只有 kitchen 內部能用
}
pub fn serve() -> String {
// 同層的模組可以用相對路徑;super 表示上一層
let dish = kitchen::cook("牛肉麵");
format!("上菜!{dish}")
}
}
fn main() {
println!("{}", restaurant::serve());
println!("{}", restaurant::kitchen::cook("炒飯"));
}
結構體的欄位也預設私有,需要逐一標 pub:
mod shop {
pub struct Product {
pub name: String, // 公開
price: u32, // 私有:只能透過方法存取
}
impl Product {
pub fn new(name: &str, price: u32) -> Product {
Product { name: name.to_string(), price }
}
pub fn price(&self) -> u32 {
self.price
}
}
}
fn main() {
let p = shop::Product::new("咖啡", 120);
println!("{} {}", p.name, p.price());
}
這是 Rust 實現「封裝」的方式:用私有欄位保護內部狀態,只透過公開方法對外提供操作。
用 use 引入路徑
每次都寫完整路徑很冗長,用 use 把名稱引入目前的作用域:
mod shapes {
pub mod circle {
pub fn area(r: f64) -> f64 {
3.14159 * r * r
}
}
}
use shapes::circle;
use std::collections::HashMap;
use std::fmt::Result as FmtResult; // 用 as 取別名,避免名稱衝突
fn main() {
println!("{:.2}", circle::area(2.0));
let _map: HashMap<String, i32> = HashMap::new();
let _ok: FmtResult = Ok(());
}
同一個路徑要引入多個名稱時可以合併,* 則引入全部(少用,容易造成名稱衝突):
use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
fn main() {
let _a: HashMap<i32, i32> = HashMap::new();
let _b: HashSet<i32> = HashSet::new();
io::stdout().flush().unwrap();
}
把模組拆成檔案
模組變大就拆成獨立檔案。在 main.rs 中寫 mod 名稱;(分號結尾),編譯器會去找對應檔案:
my_app/
├── Cargo.toml
└── src/
├── main.rs # mod restaurant;
├── restaurant.rs # 模組內容(不需要再包一層 mod)
└── restaurant/
└── kitchen.rs # restaurant.rs 中宣告 pub mod kitchen;
使用外部 crate 與 workspace
外部 crate 以 cargo add 加入後,直接用 use 套件名::... 引入。專案大到需要多個相關套件時,可以用 workspace 把它們放在同一個倉庫、共用同一份 Cargo.lock 與 target/,做法見 The Cargo Book。
實用心法
- 先把所有東西放在
main.rs,覺得太長再拆,不必一開始就設計複雜的目錄。 - 對外只公開必要的項目,其餘保持私有,日後修改內部實作比較安全。
- 想寫可被其他專案重用的程式,使用
src/lib.rs,讓main.rs只負責呼叫它。
推薦影音
模組系統
簡述:Let's Get Rusty 依照官方書籍第 7 章製作,講解套件、crate、模組、路徑與 use,對應本頁全部內容。
下一頁:測試。