I came across this issue when trying to add the impl Add<char> for String
to the standard library. But we can replicate it easily, without operator shenanigans. We start with this:
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
}
}
Simple enough. With this, the following code compiles:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Note that in this case, the second argument expression (&b
) has the type &String
. It is then deref-coerced into &str
and the function call works.
However, let's try to add the following impl:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
Now the MyAdd::add(a, &b)
expression above leads to the following error:
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>>
Why is that? To me it seems like deref-coercion is only done when there is only one function candidate. But this seems wrong to me. Why would the rules be like that? I tried looking through the specification, but I haven't found anything on argument deref coercion.