Me encontré con este problema al intentar agregar el impl Add<char> for String
a la biblioteca estándar. Pero podemos replicarlo fácilmente, sin travesuras del operador. Comenzamos con esto:
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
}
}
Suficientemente simple. Con esto, se compila el siguiente código:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Tenga en cuenta que en este caso, la segunda expresión de argumento ( &b
) tiene el tipo &String
. Luego se deserige en &str
y la llamada a la función funciona.
Sin embargo , intentemos agregar el siguiente impl:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
( Todo en el patio de recreo )
Ahora la MyAdd::add(a, &b)
expresión anterior lleva al siguiente 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>>
¿Porqué es eso? Para mí, parece que la deserción-coerción solo se realiza cuando solo hay una función candidata. Pero esto me parece mal. ¿Por qué las reglas serían así? Traté de mirar a través de la especificación, pero no he encontrado nada sobre argumentos de deserción.
impl
que aplica, puede desambiguar eligiendo el argumento de tipo utilizado en esoimpl
. En las otras preguntas y respuestas, utilicé esta capacidad para hacer que el compilador (parezca) elegir unaimpl
en el sitio de la llamada, que es algo que generalmente no puede hacer. Presumiblemente, en este caso, eso es lo que le permite hacer una coacción deref. Pero eso es solo una suposición.