Я зіткнувся з цим питанням, намагаючись додати імпл Add<char> for String
до стандартної бібліотеки. Але ми можемо повторити це легко, без операторів шнаніганів. Почнемо з цього:
trait MyAdd<Rhs> {
fn add(self, rhs: Rhs) -> Self;
}
impl MyAdd<&str> for String {
fn add(mut self, rhs: &str) -> Self {
self.push_str(rhs);
self
}
}
Досить просто. З цим складається наступний код:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Зауважте, що в цьому випадку другий вираз аргументу ( &b
) має тип &String
. Тоді воно відхиляється від деревини &str
і функція виклику працює.
Однак спробуємо додати наступне імпл:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
( Все на дитячому майданчику )
Тепер MyAdd::add(a, &b)
вираз вище приводить до наступної помилки:
error[E0277]: the trait bound `std::string::String: MyAdd<&std::string::String>` is not satisfied
--> src/main.rs:24:5
|
2 | fn add(self, rhs: Rhs) -> Self;
| ------------------------------- required by `MyAdd::add`
...
24 | MyAdd::add(a, &b);
| ^^^^^^^^^^ the trait `MyAdd<&std::string::String>` is not implemented for `std::string::String`
|
= help: the following implementations were found:
<std::string::String as MyAdd<&str>>
<std::string::String as MyAdd<char>>
Чому так? Мені здається, що дереф-примус робиться лише тоді, коли є лише один кандидат на функцію. Але це здається мені неправильним. Чому правила були б такими? Я спробував розглянути специфікацію, але нічого не знайшов на аргументі deref примусу.
impl
яка застосовується, вона може роз'єднатись, вибравши аргумент типу, який використовується в цьомуimpl
. В інших питаннях я використовував цю здатність, щоб змусити компілятор (як видається) вибратиimpl
на сайті виклику, що зазвичай не може зробити. Імовірно, в даному випадку саме це дозволяє їй робити безпричинний примус. Але це лише здогадка.