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.
fuente
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.Respuestas:
Como usted mismo explicó, el compilador trata el caso en el que solo hay uno válido
impl
especialmente, y puede usar esto para conducir la inferencia de tipos:La segunda parte es que la coerción de deref solo ocurrirá en sitios donde se conoce el tipo esperado, no ocurre especulativamente. Ver sitios de coerción en la referencia. La selección implícita y la inferencia de tipos primero deben encontrar explícitamente lo que
MyAdd::add(&str)
se esperaría, para intentar forzar el argumento a&str
.Si se necesita una solución en esta situación, utilice una expresión como
&*b
o&b[..]
ob.as_str()
para el segundo argumento.fuente