Me deparei com esse problema ao tentar adicionar o impl Add<char> for String
à biblioteca padrão. Mas podemos replicá-lo facilmente, sem travessuras de operador. Começamos com isso:
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
}
}
Simples o suficiente. Com isso, o seguinte código compila:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Note-se que, neste caso, a segunda expressão argumento ( &b
) tem o tipo &String
. Ele é então coagido &str
e a chamada de função funciona.
No entanto , vamos tentar adicionar o seguinte impl:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
Agora a MyAdd::add(a, &b)
expressão acima leva ao seguinte erro:
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>>
Por que é que? Para mim, parece que a deref-coerção é feita apenas quando há apenas um candidato a uma função. Mas isso parece errado para mim. Por que as regras seriam assim? Tentei examinar a especificação, mas não encontrei nada sobre argumento sob coerção.
impl
que se aplica, pode desambiguar escolhendo o argumento de tipo usado nelaimpl
. Nas outras perguntas e respostas, usei essa capacidade de fazer o compilador (ao que parece) escolher umimpl
no local da chamada, o que geralmente não é possível. Presumivelmente, nesse caso, é o que permite que ele faça uma coerção menor. Mas isso é apenas um palpite.