【问题标题】:Why does adding a second impl prevent deref coercion of the argument?为什么添加第二个 impl 会阻止对参数的 deref 强制?
【发布时间】:2020-04-26 06:48:46
【问题描述】:

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

请注意,在这种情况下,第二个参数表达式 (&amp;b) 的类型为 &amp;String。然后它被强制解引用到&amp;str 并且函数调用起作用。

然而,让我们尝试添加以下 impl:

impl MyAdd<char> for String {
    fn add(mut self, rhs: char) -> Self {
        self.push(rhs);
        self
    }
}

(Everything on Playground)

现在上面的MyAdd::add(a, &amp;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 的任何内容。

【问题讨论】:

  • 这让我想起了this answer(我写的)。编译器一般都知道该特征,并且当只有一个 impl 适用时,它可以通过选择在该 impl 中使用的类型参数来消除歧义。在另一个问答中,我使用此功能使编译器(似乎)在调用站点选择impl,这是它通常无法做到的。大概在 this 情况下,这就是允许它进行 deref 强制的原因。但这只是猜测。
  • Here is a comment 声明如果只找到一个 impl,编译器会“急切地确认”它,从而允许 deref 强制(除其他外)发生。多个 impl 候选人不会发生这种情况。所以我想这就是答案,但我仍然想知道更多。 rustc 书中的This chapter 可能会有所帮助,但据我所知,它并没有具体说明这一点。

标签: rust language-lawyer


【解决方案1】:

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

Here is a comment 声明如果只找到一个 impl,编译器会“急切地确认”它,从而允许 deref 强制(除其他外)发生。多个 impl 候选者不会发生这种情况。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-08
    • 2021-03-24
    • 1970-01-01
    • 1970-01-01
    • 2015-12-27
    相关资源
    最近更新 更多