深入 Rust

全面實作(blanket implementation)

有時你想讓某個行為不只套用到一個型別,而是套用到每個「已經具備某個其他性質」的型別。例如:任何你能用 Display 印出的型別,你也能用 to_string 把它轉成 String。你不會想為 i32、為 f64、為你自己的結構、為任何人將來定義的每個型別分別寫一遍這個轉換。全面實作(blanket implementation)讓你只寫一次、一次涵蓋所有型別,做法是為每個滿足某個給定界限的型別實作一個特徵。

它是一個 impl 區塊,其目標是一個被 where 子句約束的泛型型別參數,而非單一具體型別。標準函式庫真實的例子是 impl<T: Display> ToString for T { fn to_string(&self) -> String { ... } }。照字面讀:「對於每個實作了 Display 的型別 T,這是 ToString 的一個實作」。一個型別只要實作了 Display,它就自動免費得到 to_string,不需任何額外程式碼——這正是為什麼 5.to_string()、3.14.to_string()、your_struct.to_string() 都在該型別是 Display 的那一刻就能用。這個 impl 是針對界限寫的,所以它涵蓋一個開放、無上界的型別集合,包括那些還不存在的型別。你也能寫自己的:impl<T: MyTrait> MyOtherTrait for T { ... } 把 MyOtherTrait 授予每個具備 MyTrait 的東西。

為何重要:全面實作正是 Rust 一舉賦予整族型別一項能力的方式,它們在標準函式庫裡無處不在(From/Into 配對、ToString、許多迭代器轉接器)。誠實的提醒是它們與一致性(coherence)緊密互動:正因為一個全面實作涵蓋如此多型別,編譯器必須保證對同一個型別永遠不會有兩個 impl 重疊,所以你不能隨意寫兩個界限可被單一型別同時滿足的全面實作,而孤兒規則也限制了全面實作能擺在哪裡。回報很大,但全面實作也是最可能與一致性規則相撞的功能——也就是下一條。

// 真實的 std 全面實作(簡化): // impl<T: Display> ToString for T { ... } use std::fmt::Display; trait Greet { fn greet(&self) -> String; } // 一個 impl 一次涵蓋「每個」Display 型別: impl<T: Display> Greet for T { fn greet(&self) -> String { format!("hello, {self}") } } println!("{}", 42.greet()); // "hello, 42" println!("{}", "ada".greet()); // "hello, ada"

一個針對界限寫成的 impl,把特徵授予一個開放的型別集合——每個現在與未來符合界限的型別。

全面實作很強大但受一致性約束:因為它們涵蓋如此多型別,編譯器禁止任何重疊,所以你不能有兩個能被單一型別同時匹配的全面實作。它們是最可能撞上孤兒/一致性規則的功能。

又稱
blanket implgeneric impl over a bound全域實作泛型涵蓋實作