比较关联类型时为何需要PartialEq约束?
问题
存在一个以Trait为泛型参数的Wrapper结构体,其仅包含一个关联类型Trait::A的元素。
#[derive(PartialEq)] pub struct Wrapper<T: Trait>(pub(crate) <T as Trait>::A); pub trait Trait { type A: Eq + PartialEq; fn a() -> Self::A; } pub fn test<T: Trait>() { let wrapper = Wrapper::<T>(T::a()); let _r = wrapper == wrapper; }
编译时会出现如下错误:
error[E0369]: binary operation `==` cannot be applied to type `Wrapper<T>` --> src/main.rs:12:22 | 12 | let _r = wrapper == wrapper; | ------- ^^ ------- Wrapper<T> | | | Wrapper<T> | help: consider further restricting this bound | 10 | pub fn test<T: Trait + std::cmp::PartialEq>() { | +++++++++++++++++++++ For more information about this error, try `rustc --explain E0369`.
明明仅比较的是Trait::A类型,为何仍需要为T添加PartialEq约束?
解答
这是Rust自动派生PartialEq的默认规则导致的问题:当你给Wrapper<T>派生PartialEq时,编译器会自动为结构体的所有泛型参数添加上PartialEq约束,哪怕这些约束在实际比较逻辑里完全没用。
编译器自动生成的PartialEq实现大概是这样的:
impl<T: Trait + PartialEq> PartialEq for Wrapper<T> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } }
可以看到,它额外给T加了PartialEq约束,但我们的比较逻辑只用到了字段0(即<T as Trait>::A)的PartialEq能力,而Trait已经保证了A实现了PartialEq,完全不需要T本身满足这个约束。
解决方法很直接——放弃自动派生,手动实现PartialEq:
pub struct Wrapper<T: Trait>(pub(crate) <T as Trait>::A); // 手动实现PartialEq,仅要求T满足Trait即可 impl<T: Trait> PartialEq for Wrapper<T> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } // 若需要Eq特性,同样手动实现 impl<T: Trait> Eq for Wrapper<T> {} pub trait Trait { type A: Eq + PartialEq; fn a() -> Self::A; } pub fn test<T: Trait>() { let wrapper = Wrapper::<T>(T::a()); let _r = wrapper == wrapper; }
这样修改后,test函数里的比较就能正常编译,不需要给T额外添加PartialEq约束。
内容的提问来源于stack exchange,提问作者Conrado
相关产品推荐
相关产品推荐

