Why isn’t there an std::cmp::PartialEq<Self> bound on the RHS type parameter in std::cmp::PartialEq?

std::cmp::PartialEq states “that the trait itself must be implemented symmetrically and transitively: if T: PartialEq<U> and U: PartialEq<V> then U: PartialEq<T> …” (emphasis added), but this is not actually enforced in its definition. I am sure I am missing something obvious, so can someone enlighten me as to why RHS doesn’t have the trait bound std::cmp::PartialEq<Self> in addition to its ?Sized bound?

Whenever I've tried to implement that sort of circular bound chain, it confused the trait solver and threw it into an infinite loop. Perhaps it's just a limitation of the current implementation, and will work in the future. Or maybe I was just unlucky.

Good question! I would have thought that we would be hitting a circular bound in that case, but

trait Trait<U : ?Sized + Trait<Self> = Self> {}

enum A {}
impl Trait for A {}

enum B {}
impl Trait<A> for B {}
impl Trait<B> for A {}

compiles just fine, even on rust 1.0.0.


EDIT: yeah, the problem lies on the ergonomics of somebody using that generic bound: the constraint is seen to be required everywhere, instead of required on impl but assumed on use (like supertraits do). That is, having a <T, U : Trait<T>> generic fails with the missing T : Trait<U> bound.

This means that every (non-reflexive) usage of that bound would have ended up duplicated... which seems too cumbersome to be worth it.

I can be rather masochistic when it comes to “correctness” and fail to care about ergonomics as much as I should. I am working on that. Thank you for your reply.

P. S. Not really related, but I recently stumbled on one of your questions that I found to be useful as I was confused as to when one should favor supertraits over generic implementations.