Perché l'aggiunta di un secondo impl previene la coercizione dell'argomento?


10

Mi sono imbattuto in questo problema durante il tentativo di aggiungere impl Add<char> for Stringalla 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 &stre 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
    }
}

( Tutto sul parco giochi )

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.


Questo mi ricorda questa risposta (ho scritto). Il compilatore conosce il tratto in modo generico e quando ce n'è solo uno implapplicabile, può chiarire la questione scegliendo l'argomento type usato in quello impl. Nelle altre domande e risposte ho usato questa capacità per fare in modo che il compilatore (sembra) scelga un implsito 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.
Trento

2
Ecco un commento in cui si afferma che se viene trovato un solo impl il compilatore "lo conferma avidamente", il che consente che si verifichino coercizioni (tra le altre cose). Ciò non accade per più candidati impl. Quindi immagino che sia una specie di risposta, ma mi piacerebbe molto saperne di più. Questo capitolo del libro Rustc potrebbe essere d'aiuto, ma per quanto ne so, non dice specificamente qualcosa al riguardo.
Lukas Kalbertodt,

Risposte:


0

Come hai spiegato tu stesso, il compilatore tratta il caso in cui ce n'è solo uno valido implappositamente e può utilizzarlo per determinare l'inferenza del tipo:

Ecco un commento in cui si afferma che se viene trovato un solo impl il compilatore "lo conferma avidamente", il che consente che si verifichino coercizioni (tra le altre cose). Ciò non accade per più candidati impl.

La seconda parte è che la deref coercizione accadrà solo nei siti in cui è noto il tipo previsto, non avviene in modo speculativo. Vedi i siti di coercizione nel riferimento. La selezione dell'impl e l'inferenza del tipo devono prima trovare esplicitamente ciò che MyAdd::add(&str)ci si aspetterebbe, per tentare di forzare l'argomento &str.

Se è necessaria una soluzione alternativa in questa situazione, utilizzare un'espressione simile &*bo &b[..]o b.as_str()per il secondo argomento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.