孤兒規則/一致性規則(orphan / coherence rule)
想像你依賴的兩個互不相關的函式庫,都決定用各自的方式教標準的 Vec 型別如何被印出。現在你的程式同時引入兩者,在某個呼叫點,編譯器對同一個型別的同一個特徵有兩個衝突的實作——該跑哪一個?在一個容許這種事的語言裡,答案可能取決於引入順序,同一段程式碼在不同專案裡會有不同行為。Rust 徹底禁止這種情況。一致性(coherence)是這個保證:對任何(特徵、型別)配對,整個程式裡至多只有一個實作;孤兒規則則是執行它的具體限制。
孤兒規則說:你只能在「特徵或型別兩者中至少有一個是定義在你自己套件裡」時,才為某型別實作某特徵。換句話說,你可以為別人的型別實作「你的」特徵(特徵歸你),或為「你的」型別實作別人的特徵(型別歸你),但你「不能」為一個外來型別實作一個外來特徵——這個組合是個「孤兒」,兩邊都不擁有,會被駁回。所以你不能在你的套件裡寫 impl Display for Vec<i32>,因為 Display 和 Vec 都屬於標準函式庫。理由正是上面那個衝突:若任何套件能為任何外來型別實作任何外來特徵,兩個套件就可能提供互撞的 impl,一致性就會崩潰。藉由把每個 impl 綁到一個擁有它兩半之一的套件,Rust 確保標準函式庫、你的套件、與每個相依套件,都能各自新增 impl 而永遠不會踩到彼此。
為何重要:一致性正是讓特徵可靠的原因——你永遠不必煩惱哪個 impl 在生效,因為永遠只有一個。代價是那著名的挫折:「我不能在這個外部型別上實作這個外部特徵」。標準的權宜手法是 newtype 模式:把外來型別包進你自己的單欄位結構(struct MyVec(Vec<i32>)),現在型別是本地的了,於是你便可在它上面實作那個外來特徵。誠實的說法是:孤兒規則是一筆刻意的交換——它花掉你一些便利,換來一個全域保證:特徵解析在整張相依圖上都明確且穩定。
use std::fmt; // 禁止:Display 與 Vec 都是外來的(孤兒 impl)。 // impl fmt::Display for Vec<i32> { ... } // error[E0117] // 透過 newtype 模式則可:包裝器是「你的」。 struct Wrap(Vec<i32>); impl fmt::Display for Wrap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.0) } }
你不能在外來型別上實作外來特徵;用你自己的 newtype 包起來,讓其中一半變成本地的。
孤兒規則不是隨意的龜毛:它保證了整個程式裡每個(特徵、型別)配對恰有一個 impl,使特徵解析永不取決於引入順序。newtype 包裝是標準的逃生口。