Mi sono imbattuto in questo problema durante il tentativo di aggiungere impl Add<char> for String
alla libreria standard. Ma possiamo replicarlo facilmente, senza shenanigans dell'operatore. Iniziamo con questo:
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
}
}
Abbastanza semplice. Con questo, viene compilato il seguente codice:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Si noti che in questo caso, il secondo argomento expression ( &b
) ha il tipo &String
. Viene quindi forzato &str
e la chiamata di funzione funziona.
Tuttavia , proviamo ad aggiungere il seguente impl:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
Ora l' MyAdd::add(a, &b)
espressione sopra porta al seguente errore:
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>>
Perché? A me sembra che la deref-coercizione venga fatta solo quando esiste un solo candidato alla funzione. Ma questo mi sembra sbagliato. Perché le regole dovrebbero essere così? Ho provato a esaminare le specifiche, ma non ho trovato nulla sull'argomento deref coercion.
impl
applicabile, può chiarire la questione scegliendo l'argomento type usato in quelloimpl
. Nelle altre domande e risposte ho usato questa capacità per fare in modo che il compilatore (sembra) scelga unimpl
sito di chiamata, cosa che di solito non può fare. Presumibilmente in questo caso è questo che gli consente di esercitare una forte coercizione. Ma questa è solo una supposizione.