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?
Why isn’t there an std::cmp::PartialEq<Self> bound on the RHS type parameter in std::cmp::PartialEq?
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.