トレイトにより型に同名のメソッドが複数ある場合について。
メソッドの呼出記法には二通りある。
例: target.method(arg1, arg2, ...)
短く手軽だが、どのトレイトのメソッドか曖昧になりうる記法。
例: Trait::method(&target, arg1, arg2, ...)
長く煩雑だが、どのトレイトのメソッドか明確になる記法。
候補がどれもトレイト由来の場合、通常記法ではエラーになる。
以下では、型 MyType
は二つのトレイト TraitX
と TraitY
を実装しており、そのどちらもがメソッド method
を持つ。この場合、通常記法による呼出ではエラーになる。
fn main() {
let target = MyType();
assert_eq!(target.method(), "XXX");
assert_eq!(TraitX::method(&target), "TraitX");
assert_eq!(TraitY::method(&target), "TraitY");
}
struct MyType();
impl TraitX for MyType {}
impl TraitY for MyType {}
trait TraitX {
fn method(&self) -> &str {
"TraitX"
}
}
trait TraitY {
fn method(&self) -> &str {
"TraitY"
}
}
error[E0034]: multiple applicable items in scope --> src/main.rs:3:20 | 3 | assert_eq!(target.method(), "XXX"); | ^^^^^^ multiple `method` found | note: candidate #1 is defined in an impl of the trait `TraitX` for the type `MyType` --> src/main.rs:13:5 | 13 | fn method(&self) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^ note: candidate #2 is defined in an impl of the trait `TraitY` for the type `MyType` --> src/main.rs:19:5 | 19 | fn method(&self) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | 3 | assert_eq!(TraitX::method(&target), "XXX"); | ~~~~~~~~~~~~~~~~~~~~~~~ help: disambiguate the method for candidate #2 | 3 | assert_eq!(TraitY::method(&target), "XXX"); | ~~~~~~~~~~~~~~~~~~~~~~~
候補に型由来のものがあれば、通常記法ではそれが優先される。
以下では、型 MyType
がトレイト MyTrait
を実装しており、そのどちらもがメソッド method
を持つ。この場合、通常記法による呼出では前者のメソッドが優先される。
fn main() {
let target = MyType();
assert_eq!(target.method(), "MyType");
assert_eq!(MyType::method(&target), "MyType");
assert_eq!(MyTrait::method(&target), "MyTrait");
}
trait MyTrait {
fn method(&self) -> &str;
}
struct MyType();
impl MyTrait for MyType {
fn method(&self) -> &str {
"MyTrait"
}
}
impl MyType {
pub fn method(&self) -> &str {
"MyType"
}
}
この仕様はクレートのアップデートで型にメソッドが追加され、運悪く既存のものと引数などが同じだった場合、こっそりと呼出先が変更される大問題になりうる。
クレートの利用側で完全修飾構文を使えば、この問題を防げる。
しかし、クレートの定義側での対策はできない。また、必要な全ての箇所 (型を経由したトレイトメソッドの呼出箇所) でこれを行うと、地味に面倒でコードも長くなってしまう。
Cargo における SemVer 互換性の解説を見ると、これは意図された設計らしい。