12

我在尝试将 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:

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-coercion。但这对我来说似乎是错误的。为什么会有这样的规则?我尝试查看规范,但我没有找到任何关于参数 deref coercion 的内容。

4

1 回答 1

1

正如您自己解释的那样,编译器会专门处理只有一个有效的情况impl,并且可以使用它来驱动类型推断:

这是一条评论,指出如果只找到一个 impl ,编译器会“急切地确认”它,这允许 deref 强制(除其他外)发生。多个 impl 候选人不会发生这种情况。

第二部分是deref coercion只会发生在预期类型已知的站点上,它不会投机地发生。请参阅参考资料中的强制站点。Impl 选择和类型推断必须首先明确地找到MyAdd::add(&str)预期的,以尝试将参数强制为&str.

如果在这种情况下需要解决方法,请对第二个参数使用类似or的&*b表达式。&b[..]b.as_str()

于 2020-04-25T22:48:46.067 回答