トレイトどうし又はトレイトと型に同名のメソッドがあると、それらの衝突がありうる。
ここでは衝突時におこる問題とその対処法について解説している。
メソッドの呼出記法には二通りある。
例: target.method(arg1, arg2, ...)
短く手軽だが、どのトレイトのメソッドか曖昧になりうる記法。
例: SomeTrait::method(&target, arg1, arg2, ...)
長く煩雑だが、どのトレイトのメソッドか明確になる記法。
同名のメソッドに型由来のものがあれば、通常記法ではそれが優先される。
以下では、型 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"
}
}
上流クレートで追加したメソッドが下流クレートの呼出箇所で衝突する危険性について。
引数型などが一致しない場合、コンパイルエラーになる!
引数型などが一致した場合、静かに呼出先が変更される!!
残念ながらあまり強力な方法はない…。
上流側: 困難 (強いて言うなら下流側の努力を祈るくらい…)
下流側: 完全修飾構文の使用 (地味にコードが長く読みにくくなる…)
同名のメソッドがどちらもトレイト由来の場合、通常記法ではエラーになる。
以下では、型 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");
| ~~~~~~~~~~~~~~~~~~~~~~~
上流クレートで追加したメソッドが下流クレートの呼出箇所で衝突する危険性について。
型がまざる場合とは異なり、トレイトどうしの場合は一律でコンパイルエラーになる。
型がまざる場合よりやや状況はよい。
上流側: 型にトレイトのメソッドをコピー (よく使う型ではかなり有効)
下流側: 完全修飾構文の使用 (こちらは型が混ざる場合と同様)
型にトレイトのメソッドをコピーする手法について。
これは『型 vs トレイト』で紹介した型優先のルールの応用である。
例: Range 型には RangeBounds トレイト由来の contains メソッド、そして型それ自身の contains メソッドの両方がある。後者は前者への中継のみを行うメソッドであり、一見するとこれは無意味だが、これによりどんなトレイト実装が適用されているかを気にせず Range の contains を通常記法で一意に呼び出せるようになる。
Cargo における SemVer 互換性の解説より。
ここで紹介したメソッド追加の変更は「許容されうる破壊的変更」扱いらしい。