J'ai rencontré ce problème en essayant d'ajouter l'impl Add<char> for String
à la bibliothèque standard. Mais nous pouvons le reproduire facilement, sans manigances d'opérateur. Nous commençons par ceci:
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
}
}
Assez simple. Avec cela, le code suivant se compile:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Notez que dans ce cas, la seconde expression d'argument ( &b
) a le type &String
. Il est ensuite dé-contraint &str
et l'appel de fonction fonctionne.
Cependant , essayons d'ajouter l'impl suivant:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
Maintenant, l' MyAdd::add(a, &b)
expression ci-dessus conduit à l'erreur suivante:
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>>
Pourquoi donc? Il me semble que la deref-coercition ne se fait que lorsqu'il n'y a qu'une seule fonction candidate. Mais cela me semble faux. Pourquoi les règles seraient-elles ainsi? J'ai essayé de parcourir la spécification, mais je n'ai rien trouvé sur l'argument deref coercion.
la source
impl
qui s'applique, il peut lever l'ambiguïté en choisissant l'argument type utilisé dans celaimpl
. Dans les autres questions et réponses, j'ai utilisé cette capacité pour que le compilateur (apparemment) choisisse unimpl
sur le site d'appel, ce qui n'est généralement pas possible. Vraisemblablement dans ce cas, c'est ce qui lui permet d'exercer une contrainte deref. Mais ce n'est qu'une supposition.Réponses:
Comme vous l'avez expliqué vous-même, le compilateur traite le cas où il n'y en a qu'un seul
impl
spécialement valide , et peut l'utiliser pour conduire l'inférence de type:La deuxième partie est que la coercition deref ne se produira que sur les sites où le type attendu est connu, cela ne se produit pas de manière spéculative. Voir sites de coercition dans la référence. La sélection d'impl et l'inférence de type doivent d' abord trouver explicitement ce qui
MyAdd::add(&str)
serait attendu, pour tenter de contraindre l'argument à&str
.Si une solution de contournement est nécessaire dans cette situation, utilisez une expression comme
&*b
ou&b[..]
oub.as_str()
pour le deuxième argument.la source