Я сталкивался с этой проблемой при попытке добавить impl Add<char> for String
в стандартную библиотеку. Но мы можем повторить это легко, без махинаций оператора. Начнем с этого:
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
}
}
Достаточно просто. С этим компилируется следующий код:
let a = String::from("a");
let b = String::from("b");
MyAdd::add(a, &b);
Обратите внимание, что в этом случае выражение второго аргумента ( &b
) имеет тип &String
. Затем он принудительно вызывается, &str
и вызов функции работает.
Тем не менее , давайте попробуем добавить следующее значение:
impl MyAdd<char> for String {
fn add(mut self, rhs: char) -> Self {
self.push(rhs);
self
}
}
Теперь приведенное MyAdd::add(a, &b)
выше выражение приводит к следующей ошибке:
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>>
Это почему? Мне кажется, что дереф-принуждение выполняется только тогда, когда есть только один кандидат на функцию. Но это мне кажется неправильным. Почему правила таковы? Я пытался просмотреть спецификацию, но я не нашел ничего по поводу доводов deref.
источник
impl
применяется только один , он может устранить неоднозначность, выбрав аргумент типа, используемый в этомimpl
. В других вопросах и ответах я использовал эту возможность, чтобы компилятор (кажется) выбирал объектimpl
на сайте вызова, что обычно невозможно. Предположительно, в этом случае это то, что позволяет делать разовое принуждение. Но это только предположение.Ответы:
Как вы сами объяснили, компилятор обрабатывает случай, когда существует только один допустимый
impl
, и может использовать это для вывода типа диска:Вторая часть заключается в том, что принудительное приведение в действие будет происходить только в тех местах, где известен ожидаемый тип, а не спекулятивно. Смотрите сайты принуждения в ссылке. Выбор Impl и вывод типа должны сначала явно найти то,
MyAdd::add(&str)
что ожидалось, чтобы попытаться привести аргумент к&str
.Если в этой ситуации требуется обходной путь, используйте выражение наподобие
&*b
или&b[..]
илиb.as_str()
для второго аргумента.источник