Search code examples
swiftgenericsswift-protocolsswift-extensions

Why 'there cannot be more than one conformance, even with different conditional bounds'?


I hoped that Swift gives me the ability to create an extension for type with specified conditions in where block. I imagined that I can extend the same generic type with different extensions dependent on concrete generic type value (T). But not. Following example demonstrates my problem:

protocol P {    
    associatedtype Prop 
    var property: Prop { get }
}

enum E<T: P> {
   case single(T)
   case double(T)
}

extension E: P where T.Prop == Int {
   var property: Int {
      switch self {
      case .single(let o): return o.property
      case .double(let o): return o.property * 2
      }
   }
}

extension E: P where T.Prop == String {
   var property: String {
      switch self {
      case .single(let o): return o.property
      case .double(let o): return o.property + o.property
      }
   }
}

struct Int4: P {
   var property: Int {
       return 4
   }
}

struct StringHello: P {
    var property: String {
        return "Hello" 
    }
}

print(E.single(Int4()).property)
print(E.double(StringHello()).property)

Following error and note are the result of the compilation.

error: conflicting conformance of 'E' to protocol 'P'; there cannot be more than one conformance, even with different conditional bounds extension E: P where T.Prop == String {

note: 'E' declares conformance to protocol 'P' here extension E: P where T.Prop == Int {

Is it really impossible? Why? What can I do with my code to succeed?


Some details to demonstrate the problem in my real situation.
I have some generic enum, which is used with many different wrapped types.

enum Color<T> {
  case red(T), green(T)

  func map<T2>(_ transform: (T) -> T2) -> Color<T2> {
    switch self {
    case .red(let o): return .red(transform(o))
    case .green(let o): return .green(transform(o))
    }
  }
}

Very often, I need different extensions for Color to conform it to different protocols depending on the wrapped type. Sometimes these protocols have the same base (super) protocol and as a result, I have the current problem. Sometimes I cant extend Color to conform this base (super) protocol for all deriving protocols because I need different implementations.


Solution

  • Is it impossible? Yes and no. It's not currently possible in Swift, as it has been implemented. It is in principle possible to be implemented.

    The name for this is "overlapping conformances", and it was explicitly and purposely rejected. You can find the rationale in the "Alternatives considered" section of SE-0143 Conditional conformances. The TL;DR is: because it's really complicated.

    Without knowing more about what exactly you were trying to use this for, there's not much direction we can provide.