メソッド解決の競合

固有メソッドと拡張メソッドの間で名前の競合があると、何が起こるでしょうか?

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

mod ext {
    pub trait CountOnesExt {
        fn count_ones(&self) -> u32;
    }

    impl CountOnesExt for i32 {
        fn count_ones(&self) -> u32 {
            let value = *self;
            (0..32).filter(|i| ((value >> i) & 1i32) == 1).count() as u32
        }
    }
}
fn main() {
    pub use ext::CountOnesExt;
    // どの `count_ones` メソッドが呼び出されるでしょうか?
    // `CountOnesExt` のものですか? それとも `i32` の固有メソッドですか?
    assert_eq!((-1i32).count_ones(), 32);
}
  • 外部の型に、新しいバージョンで、私たちの拡張メソッドと同じ名前の 新しい固有メソッドが追加されることがあります。

    質問: 上の例では何が起こるでしょうか? コンパイラエラーになるでしょうか? 2 つのメソッドのいずれかに、より高い優先順位が与えられるでしょうか? それはどちらでしょうか?

    CountOnesExt::count_ones の本体に panic!("Extension trait"); を追加して、 どちらのメソッドが呼び出されているのかを明確にしてください。

  • Rust 言語のユーザーがあらゆる場合にどのメソッドを使うかを手動で指定しなくても よいように、メソッドが最初にどのように「選ばれる」かには 優先順位の仕組みがあります:

    • 不変(&self)が先
      • 固有(型の impl ブロックで定義されたメソッド)がトレイト(trait の impl によって追加されたメソッド)より先。
    • 可変(&mut self)が次
      • 固有がトレイトより先。 同じ名前のすべてのメソッドがそれぞれ異なる可変性を持ち、かつ 固有メソッドまたはトレイトメソッドのいずれかとして定義されていて、 重複がなければ、コンパイラにとって判定は容易になります。 ただし、これはユーザーにある程度の曖昧さをもたらします。依存している メソッドがなぜ期待どおりの振る舞いをしないのか分からず、混乱するかも しれません。可能であれば、この仕組みに頼るのではなく名前の競合を 避けてください。

    実演: CountOnesExt::count_ones のシグネチャと実装を fn count_ones(&mut self) -> u32 に変更し、それに合わせて呼び出しも 修正してください:

    #![allow(unused)]
    fn main() {
    // Copyright 2025 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    assert_eq!((&mut -1i32).count_ones(), 32);
    }

    固有メソッドではなく CountOnesExt::count_ones が呼び出されます。これは、 &mut self のほうが、固有メソッドで使われる &self よりも優先順位が 高いためです。

    同じ型に対して不変の固有メソッドと可変のトレイトメソッドが存在する場合、 呼び出し箇所でどちらを使うかを指定できます。(&<value>).count_ones() を使って 不変の(より高い優先順位の)メソッドを取得するか、 (&mut <value>).count_ones()

    学生には、メソッド解決 の詳細について Rust リファレンスを参照するよう 案内してください。

  • 拡張トレイトのメソッドと固有メソッドの間で名前の競合が起きないようにして ください。Rust のメソッド解決アルゴリズムは複雑であり、あなたのコードの ユーザーを驚かせる可能性があります。

さらに詳しく

  • Rust のメソッド解決アルゴリズムで使われる優先順位検索と、自動 Deref の 相互作用は、主にマクロ生成コードの文脈において、stable ツールチェーン上で 特殊化 をエミュレートするために利用できます。具体的な詳細については “Autoref Specialization” を参照してください。