Coherence and blanket impls for generic newtypes

I would like to be able to write something like the following:

#[derive(PartialEq)]
struct Wrapper<T>(T);

impl<T: PartialEq> PartialEq<T> for Wrapper<T> {
    fn eq(&self, other: &T) -> bool {
        self.0.eq(other)
    }
}

impl<T: PartialEq> PartialEq<Wrapper<T>> for T {
    fn eq(&self, other: &Wrapper<T>) -> bool {
        self.eq(other.0)
    }
}

Unfortunately, this isn't allowed by the current coherence rules:

error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Wrapper<T>`)
  --> foo.rs:10:6
   |
10 | impl<T: PartialEq> PartialEq<Wrapper<T>> for T {
   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Wrapper<T>`)
   |
   = note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local, and no uncovered type parameters appear before that first local type
   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last

Is there any interest in relaxing the coherence rules to allow this? E.g. an existing RFC or pre-RFC somewhere? Is it even possible to allow this in a coherent way?

2 Likes

Ah so there's a fairly obvious answer: that impl would conflict with this:

struct Foo;

impl<T> PartialEq<T> for Foo {
    fn eq(&self, other: &T) -> bool {
        false
    }
}
1 Like

I usually end up implementing Deref<Target=T> for my newtypes, so that I can say something like this:

if oldtype == *newtype { /* ... */ }

It recognizes that semantic information is stripped, but is relatively ergonomic in practice. AsRef<T>, From<T>, and Into<T> are also commonly useful.

Not a bad idea. I have a few cases where that won't work because the wrapper actually applies some transformation to the wrapped value, and a few other cases where the PartialEq requirement actually comes from some other trait bound. To handle that case I usually end up macroing a bunch of impls for specific types, but it would be nice if I could somehow just write one generic impl.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.