为什么我需要在`implforeignTrait<LocalType> for T`中“覆盖”T[E0210]

msr*_*rd0 10 traits rust

今天我遇到了一条非常奇怪的错误消息,我很难理解。考虑这个简单的类似地图条目的结构:

struct Entry<K, V> {
    key: K,
    value: V
}
Run Code Online (Sandbox Code Playgroud)

现在,我想实现with self 和 with juststd::cmp之间的所有特征。我们暂时先关注一下。这两个实现工作得很好:Entry<K, V>KPartialEq

impl<K: PartialEq, V> PartialEq for Entry<K, V> { /* ... */ }
impl<K: PartialEq, V> PartialEq<K> for Entry<K, V> { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

但最后一个让我很难受(游乐场

impl<K: PartialEq, V> PartialEq<Entry<K, V>> for K {
    fn eq(&self, other: &Entry<K, V>) -> bool {
        self.eq(&other.key)
    }
}
Run Code Online (Sandbox Code Playgroud)

据我所知,错误消息声称我将使用非本地类型作为外部特征的第一个参数。但是,Entry它是在同一文件中本地定义的。

错误[ E0210 ]: 当类型参数出现在第一个局部类型 ( )K之前时,它必须被另一个类型覆盖Entry<K, V>

--> src/lib.rs:6:6
  |
6 | impl<K: PartialEq, V> PartialEq<Entry<K, V>> for K {
  |      ^ type parameter `K` must be covered by another type when it appears before the first local type (`Entry<K, V>`)
  |
Run Code Online (Sandbox Code Playgroud)

注意:只有当至少有一个要实现的类型是本地类型,并且在第一个本地类型之前没有未覆盖的类型参数出现时,才能实现外部特征
注意:在这种情况下,“之前”指的是以下顺序:impl<..> ForeignTrait<T1, ..., Tn> for T0,哪里T0是第一个,哪里Tn是最后一个

有人可以解释为什么我收到此错误消息,错误和特别是未发现的错误意味着什么以及为什么不允许这种实现?

msr*_*rd0 13

错误消息非常糟糕,但仔细阅读E0210的完整描述至少可以解释发生了什么:

考虑一个实现:

impl<P1, ..., Pm> ForeignTrait<T1, ..., Tn> for T0 { ... }
Run Code Online (Sandbox Code Playgroud)

其中和P1, ..., Pm的类型参数是类型。其中一个类型必须是本地类型(这是另一个孤儿规则,请参阅E0117的解释)。implT0, ..., TnT0, ..., Tn

以下两项必须为真:

  1. 至少其中一种类型T0..=Tn必须是本地类型。让我们Ti成为第一个这样的类型。
  2. 任何未覆盖的类型参数P1..=Pm不得出现在T0..Ti(不包括Ti)中。

现在,回顾一下相关的实现,它被声明为

impl<K: PartialEq, V> PartialEq<Entry<K, V>> for K // error
Run Code Online (Sandbox Code Playgroud)

P1=K, P2=V因此给出和的映射T0=K, T1=Entry<K, V>Ti因此T1会触发错误消息。覆盖就像将其放入单元素元组一样简单,因此这种语法完全没问题:

impl<K: PartialEq, V> PartialEq<Entry<K, V>> for (K,) // ok
Run Code Online (Sandbox Code Playgroud)

一般来说,覆盖类型可以通过将其放入元组((K,))或将其用作类型参数(SomeType<K>)来完成,但不能使用引用(&K)或框(Box<K>)。

仅在实施RFC 2451中给出了对此的可理解且合理的解释,该解释建议将其作为错误消息的一部分:

如果要实现的特征或类型对于当前包来说是本地的,Rust 的孤儿规则总是允许 impl。因此,我们不能允许impl<T> ForeignTrait<LocalTypeCrateA> for T,因为它可能与impl<T> ForeignTrait<T> for LocalTypeCrateB我们始终允许的另一个箱子写入发生冲突。

我希望 rustc 能够采用该错误消息,而不是吐出它目前所做的神秘错误消息。