--- name: rust-design-patterns description: > Rust 慣用設計模式技能。涵蓋 Builder Pattern(型別安全構建器)、 Newtype Pattern(型別封裝與 Deref)、Typestate Pattern(編譯期狀態機)、 RAII Guard Pattern(資源管理)、Interior Mutability(Cell/RefCell/Mutex)、 Strategy Pattern(trait objects vs enum dispatch)、 Command Pattern、Observer Pattern(回呼與事件系統)、 Extension Trait Pattern、Sealed Trait Pattern、From/Into 慣用轉換。 觸發關鍵詞:Rust design pattern, builder, newtype, typestate, RAII, interior mutability, strategy pattern, trait object, enum dispatch, sealed trait, extension trait, From Into, guard pattern --- # Rust 慣用設計模式 ## 適用場景 - 設計新的 struct/API 時,需要選擇合適的建構方式(Builder / Typestate) - 需要對原始型別加上語意約束或防止混用(Newtype) - 管理需要確定釋放的資源(檔案、鎖、連線)→ RAII Guard - 在不可變結構中仍需局部可變性 → Interior Mutability - 需要執行時多態策略切換 → Strategy(trait object / enum dispatch) - 限制 trait 只能在本 crate 實作 → Sealed Trait - 為外部型別追加方法 → Extension Trait - 事件驅動回呼架構 → Observer / Command Pattern - 型別間慣用轉換 → From / Into / TryFrom ## 核心知識 ### 模式索引表 | 模式 | 用途 | 複雜度 | |------|------|--------| | Newtype | 為既有型別建立語意包裝,防止混用 | ★☆☆ | | From/Into | 型別間慣用零成本轉換 | ★☆☆ | | Builder (基礎) | 多欄位 struct 的鏈式建構 | ★★☆ | | Typestate Builder | 編譯期強制必填欄位順序 | ★★★ | | RAII Guard | 利用 Drop 自動釋放資源 | ★★☆ | | Interior Mutability | 在 `&self` 下進行可變操作 | ★★☆ | | Strategy (trait object) | 執行時動態切換行為 | ★★☆ | | Strategy (enum dispatch) | 靜態分派、無堆積配置 | ★★☆ | | Sealed Trait | 限制 trait 只能在本 crate 實作 | ★★☆ | | Extension Trait | 為外部型別追加方法 | ★☆☆ | | Observer / Callback | 事件訂閱與通知 | ★★★ | | Command | 將操作封裝為物件,支援 undo | ★★☆ | ### 各模式詳解 #### 1. Newtype Pattern 將原始型別包進單欄位 tuple struct,獲得: - **型別安全**:`UserId(u64)` 與 `OrderId(u64)` 編譯期不可混用 - **封裝**:可為 newtype 實作自訂方法與 trait - **零成本**:編譯後與原始型別完全相同的記憶體佈局 ```rust // 基本定義 struct Meters(f64); struct Seconds(f64); // 透過 Deref 暴露內部值(謹慎使用) impl std::ops::Deref for Meters { type Target = f64; fn deref(&self) -> &f64 { &self.0 } } ``` **搭配 From/Into**:提供慣用的型別轉換,避免手動 `.0` 存取。 ```rust impl From for Meters { fn from(v: f64) -> Self { Meters(v) } } // 自動獲得 Into for f64 ``` #### 2. Builder Pattern(基礎) 適用於有多個可選欄位的 struct: ```rust struct Config { host: String, port: u16, max_retries: u32, } struct ConfigBuilder { host: String, port: u16, max_retries: u32, } impl ConfigBuilder { fn new(host: impl Into) -> Self { /* ... */ } fn port(mut self, port: u16) -> Self { self.port = port; self } fn max_retries(mut self, n: u32) -> Self { self.max_retries = n; self } fn build(self) -> Config { /* ... */ } } ``` 也可使用 `typed-builder` crate 以 derive macro 自動生成。 #### 3. Typestate Builder 利用泛型參數的**零大小型別 (ZST)** 在編譯期追蹤狀態: ```rust struct NoHost; struct HasHost; struct RequestBuilder { host: Option, _state: std::marker::PhantomData, } impl RequestBuilder { fn host(self, h: impl Into) -> RequestBuilder { /* 狀態轉移 */ } } impl RequestBuilder { fn send(self) -> Response { /* 只有設定 host 後才能呼叫 */ } } ``` **優點**:錯誤在編譯期捕獲,不需 runtime 檢查。 #### 4. RAII Guard Pattern Rust 的 `Drop` trait 保證離開作用域時自動執行清理: ```rust struct TimerGuard { start: std::time::Instant, label: String } impl Drop for TimerGuard { fn drop(&mut self) { println!("{}: {:?}", self.label, self.start.elapsed()); } } ``` **常見用途**:MutexGuard、檔案鎖、計時器、交易回滾。 #### 5. Interior Mutability | 型別 | 執行緒安全 | 檢查時機 | 典型場景 | |------|-----------|---------|---------| | `Cell` | 否 | 編譯期(Copy) | 簡單計數器 | | `RefCell` | 否 | 執行時借用 | 圖結構、快取 | | `Mutex` | 是 | 執行時鎖 | 多執行緒共享 | | `RwLock` | 是 | 讀寫鎖 | 讀多寫少 | | `OnceCell/LazyLock` | 是 | 僅初始化一次 | 全域單例 | ```rust use std::cell::RefCell; struct Cache { data: RefCell>, } impl Cache { fn get_or_insert(&self, key: &str) -> String { // &self 但可修改內部 data let mut map = self.data.borrow_mut(); map.entry(key.to_owned()) .or_insert_with(|| expensive_compute(key)) .clone() } } ``` #### 6. Strategy Pattern **Trait object 版本**(動態分派,適合插件架構): ```rust trait Compressor: Send + Sync { fn compress(&self, data: &[u8]) -> Vec; } fn process(compressor: &dyn Compressor, data: &[u8]) -> Vec { compressor.compress(data) } ``` **Enum dispatch 版本**(無堆積配置,效能更佳): ```rust enum Compressor { Gzip, Lz4, Zstd } impl Compressor { fn compress(&self, data: &[u8]) -> Vec { match self { Self::Gzip => { /* ... */ } Self::Lz4 => { /* ... */ } Self::Zstd => { /* ... */ } } } } ``` **選擇原則**:變體固定 → enum;需外部擴展 → trait object。 #### 7. Sealed Trait Pattern 防止下游 crate 實作你的 trait: ```rust mod private { pub trait Sealed {} } pub trait MyTrait: private::Sealed { fn method(&self); } // 僅在本 crate 內實作 Sealed → 外部無法實作 MyTrait impl private::Sealed for MyStruct {} impl MyTrait for MyStruct { fn method(&self) { /* ... */ } } ``` #### 8. Extension Trait Pattern 為外部型別追加方法而不需要 Newtype: ```rust pub trait StrExt { fn is_blank(&self) -> bool; } impl StrExt for str { fn is_blank(&self) -> bool { self.trim().is_empty() } } // 使用:use crate::StrExt; " ".is_blank() ``` #### 9. Observer / Callback Pattern ```rust type Callback = Box; struct EventBus { listeners: Vec>, } impl EventBus { fn subscribe(&mut self, cb: impl Fn(&T) + Send + 'static) { self.listeners.push(Box::new(cb)); } fn emit(&self, event: &T) { for listener in &self.listeners { listener(event); } } } ``` #### 10. Command Pattern ```rust trait Command { fn execute(&mut self); fn undo(&mut self); } ``` 將操作封裝為實作 `Command` 的結構體,支援 undo/redo 堆疊。 ## 程式碼範例 完整可編譯範例見 `examples/` 目錄: - **basic.rs** — Newtype + From/Into + Builder(基礎版) - **intermediate.rs** — Typestate Builder + RAII Guard + Interior Mutability - **advanced.rs** — Strategy (trait object + enum) + Sealed Trait + Extension Trait + Observer ## 常見錯誤對照表 | 錯誤訊息 | 原因 | 修復方式 | |----------|------|---------| | `the trait Sealed is not implemented for MyType` | 嘗試在外部 crate 實作 Sealed Trait | 這是設計意圖;改用該 crate 提供的型別 | | `already borrowed: BorrowMutError` (runtime panic) | `RefCell` 同時存在 `borrow()` 與 `borrow_mut()` | 縮小借用範圍,或改用 `Cell` / `Mutex` | | `cannot move out of self which is behind a mutable reference` | Builder 方法用 `&mut self` 但嘗試消耗 self | 改用 `self`(owned)作為 builder 方法接收者 | | `unused type parameter H` | Typestate 泛型參數未被實際欄位使用 | 新增 `PhantomData` 欄位 | | `trait objects cannot have associated functions` | trait 內含 `fn new() -> Self` 等方法,無法做 `dyn Trait` | 加上 `where Self: Sized` 或移至 companion function | | `cannot return reference to temporary value` | RAII guard 的 Deref 回傳了臨時值的參考 | 確保 guard 內部持有擁有權的資料,Deref 回傳其參考 | | `the trait bound Send is not satisfied` | 將 `Rc>` 用於多執行緒 | 改用 `Arc>` | ## Cargo.toml 依賴模板 ```toml [dependencies] # derive_more 2.0:自動產生 From, Deref, Display 等 derive derive_more = { version = "2.0", features = ["from", "deref", "display"] } # typed-builder:以 derive macro 生成型別安全 builder typed-builder = "0.20" # 以下為選用 thiserror = "2" # 錯誤型別 derive once_cell = "1.20" # LazyLock 替代方案(std 已有 LazyLock,此為向後相容) ``` ## 參考來源 1. [Rust Design Patterns Book](https://rust-unofficial.github.io/patterns/) — 社群維護的模式集 2. [The Rust Book — Ch.17 OOP Features](https://doc.rust-lang.org/book/ch17-00-oop.html) — trait objects 與動態分派 3. [Rust API Guidelines — Type Safety](https://rust-lang.github.io/api-guidelines/type-safety.html) — Newtype 與型別安全 4. [Cliffle — Typestate Pattern](https://cliffle.com/blog/rust-typestate/) — Typestate 深入教學 5. [Rust Reference — Drop](https://doc.rust-lang.org/reference/destructors.html) — RAII 與 Drop 語意 6. [derive_more 文件](https://docs.rs/derive_more/2.0.1/derive_more/) — 自動 derive 工具 7. [typed-builder 文件](https://docs.rs/typed-builder/0.20.0/typed_builder/) — 型別安全 Builder derive