拡張トレイト

外部の型を新しいメソッドで拡張したいと便利な場合があります。たとえば、 メソッド呼び出し構文 s.is_palindrome() を使って、文字列が回文かどうかを コードで判定できるようにしたい、といった場合です。

その場合、impl ブロックを使いたくなるかもしれません。

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

// 🛠️❌
impl str {
    pub fn is_palindrome(&self) -> bool {
        self.chars().eq(self.chars().rev())
    }
}

しかし、Rust コンパイラはそれを許可しません。とはいえ、この制約は 拡張トレイト パターン を使うことで回避できます。

  • Rust のアイテム(トレイトでも型でも)は、次のように呼ばれます。

    • 現在のクレートで定義されていない場合は foreign
    • 現在のクレートで定義されている場合は local この区別は、コヒーレンスとオーファンルール に大きく関わります。この点は、 コースのこのセクションで詳しく見ていきます。
  • この例をコンパイルして、出力されるコンパイラエラーを示してください。

    コンパイラのエラーメッセージが、どのように拡張トレイトパターンへと導いてくれる のかを強調してください。

  • Rust における多くの型システム上の制約が、曖昧さ を防ぐことを目的としている ことを説明してください。

    もし外部の型に新しい固有メソッドを定義できるとしたら、どうなるでしょうか。 依存関係ツリー内の異なるクレートが、同じ外部の型に対して同じ名前の異なる メソッドを定義してしまうかもしれません。

    曖昧さが入り込む余地があるなら、それを解消する方法が必要になります。 曖昧さの解消が暗黙的に行われると、驚くような、あるいは予期しない挙動に つながる可能性があります。曖昧さの解消が明示的に行われる場合、コードを読む 開発者の認知負荷が増える可能性があります。

    さらに、あるクレートが外部の型に新しい固有メソッドを定義するたびに、明示的な 曖昧さの解消を導入せざるを得なくなるため、あなたの コードでコンパイルエラーが 発生する可能性があります。

    Rust は、外部の型に新しい固有メソッドを定義すること自体を禁止することで、 この問題そのものを避けることにしています。

  • 他の言語(例: Kotlin、C#、Swift)では、既存の型にメソッドを追加できます。 これはしばしば「拡張メソッド」と呼ばれます。これにより、潜在的な曖昧さや 大域的な推論の必要性に関して、異なるトレードオフが生じます。