項目13:デフォルト実装を用いて、実装しなければならないトレイトメソッドを最小限にしよう
LT;DR
トレイトを定義する際には、標準ライブラリの例に習って、デフォルト実装を持つメソッド(と適切なトレイト境界)を追加 することで、「実装する人」には最小限の API のみを、「利用する人」には便利で網羅的な API を提供しよう
hr.icon
トレイト の設計者は「実装する人」と「利用する人」を考慮する必要がある これにより、以下のような対立関係が生まれる
「実装する人」を楽にするには、目標を達成できる 最低限のメソッドだけ提供 する
「利用する人」を楽にするには、トレイトが利用されるユースケースをすべてカバーした 様々なメソッドを提供 する
ExactSizeIterator: 繰り返し対象の要素数が事前に分かっている イテレータ is_empty はトレイトメソッド len に依存したデフォルト実装を持つ
code:rs
fn is_empty(&self) -> bool {
self.len() == 0
}
上記を踏まえると、「トレイトは、実装しなければ少数の必須メソッドと、それ以外の多数のデフォルト実装を持つメソッドを持つのが良い」
標準ライブラリでも広く採用されている
必須メソッドは next だけだが、デフォルト実装を持つメソッドは 50 を超えている
また、Iterator の定義では、トレイト境界 とデフォルト実装を有効に組み合わせていることが確認できる たとえば cloned メソッドは、要素(Item)が Clone を実装している場合のみ利用できる
code:rs
fn cloned<'a, T>(self) -> Cloned<Self>
where
T: 'a + Clone,
Self: Sized + Iterator<Item = &'a T>,
{
Cloned::new(self)
}
トレイトは最初のバージョンをリリースした後であっても、「通常は」新しいメソッドを安全に追加できる
「通常は」としたのは、新しいメソッドの名前がある型が実装しているトレイトのメソッドと衝突する可能性があるため
衝突してもコンパイルエラーにはならないが、名前解決 ルールにより呼ばれる結果が変わる可能性がある これを回避するため、「利用する人」はどの具象型のメソッドを呼び出すか明示(固有実装)する必要がある code:rs
<Concrete as Trait>::method()
衝突しなければ、「実装する人」と「利用する人」の双方に対して 後方互換性 を満たす