About conforming types you don't own to protocols you don't own

I think I loosely understand the rationale behind the general advice:
"Do not conform types you don't own to protocols you don't own."

But I'd like to see a more in depth discussion. Is it officially documented somewhere? Is there a name for it? Are there any exceptions?

For example this seems harmless to me (but perhaps it isn't?)
extension CGPoint : ExpressibleByArrayLiteral {
  public init(arrayLiteral elements: CGFloat...) {
    precondition(elements.count == 2)
    self.init(x: elements[0], y: elements[1])
  }
}

extension CGSize : ExpressibleByArrayLiteral {
  public init(arrayLiteral elements: CGFloat...) {
    precondition(elements.count == 2)
    self.init(width: elements[0], height: elements[1])
  }
}

extension CGRect : ExpressibleByArrayLiteral {
  public init(arrayLiteral elements: CGFloat...) {
    precondition(elements.count == 4)
      self.init(x: elements[0], y: elements[1],
                width: elements[2], height: elements[3])
  }
}

It's harmless until Apple decides to also make them ExpressibleByArrayLiteral, and you update the OS. Which conformance will be used? The one defined in Foundation, or the one defined in your code? What if one of the libraries you use adds the same conformance?

If you own either the type, or the protocol, you will be able to make sure that there's only one conformance

Here's a thread talking about it Retroactive Conformances vs. Swift-in-the-OS

1 Like

Just as a note, Rust makes this directly a compiler error

One restriction to note with trait implementations is that we can implement a trait on a type only if either the trait or the type is local to our crate. For example, we can implement standard library traits like Display on a custom type like Tweet as part of our aggregator crate functionality, because the type Tweet is local to our aggregator crate. We can also implement Summary on Vec<T> in our aggregator crate, because the trait Summary is local to our aggregator crate.

But we can’t implement external traits on external types. For example, we can’t implement the Display trait on Vec<T> within our aggregator crate, because Display and Vec<T> are defined in the standard library and aren’t local to our aggregator crate. This restriction is part of a property of programs called coherence , and more specifically the orphan rule , so named because the parent type is not present. This rule ensures that other people’s code can’t break your code and vice versa. Without the rule, two crates could implement the same trait for the same type, and Rust wouldn’t know which implementation to use.

from https://doc.rust-lang.org/book/ch10-02-traits.html

4 Likes