Coherence: allowing generic parameters bound by local trait?

Is there a fundamental reason this is not allowed? It seems like this should work conceptually.

trait Local {
    fn stringify(&self) -> String;

impl<T: Local> From<T> for String {
    fn from(self) -> String {

This results in:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
 --> src/
5 | impl<T: Local> From<T> for String {
  |      ^ type parameter `T` must be used as the type parameter for some local type
  = note: only traits defined in the current crate can be implemented for a type parameter
1 Like

I stumbled upon such a problem too, but if you implement such a trait for a foreign type, you get to implement foreign traits for foreign types.

I solved it by defining macro rules and implementing all traits for all local types by macro instead.

If you had:

impl Local for i32 {}

then you'd end up with duplicate impl From<i32> for String. I suppose the compiler could check if Local is implemented only for your types, but it would be departure from traits' current behavior where rules prevent clashes in theory, not merely via checks in practice.


This makes sense, thanks for the explanation!

More specifically, proving this ok requires proving that Local is not implemented for any upstream type. (Because it's perfectly fine to introduce a new From impl to a type in a new upstream release.)

While this is doable because the check is entirely local, negative reasoning about coherence makes it both more complicated to check and more of a compatibility hazard.

For an example of negative reasoning gone wrong, see the current issues with Pin around e.g. malicious DerefMut impls for &T. Even in the discussion around negative impls and negative reasoning in the type system that spawned from that discussion, negative reasoning has been scoped to be exclusively positive (i.e. a type has explicitly promised to never implement this trait, not just doesn't implement it right now).

1 Like