Search code examples
swiftswift-protocolscustomstringconvertible

How to resolve overlapping conformances of CustomStringConvertible


Based on this article by John Sundell I have following struct:

protocol Identifiable {
    associatedtype RawIdentifier: Codable, Hashable = String

    var id: Identifier<Self> { get }
}

struct Identifier<Value: Identifiable>: Hashable {
    let rawValue: Value.RawIdentifier

    init(stringLiteral value: Value.RawIdentifier) {
        rawValue = value
    }
}

extension Identifier: ExpressibleByIntegerLiteral
          where Value.RawIdentifier == Int {
    typealias IntegerLiteralType = Int

    init(integerLiteral value: IntegerLiteralType) {
        rawValue = value
    }
}

It can be either String or Int. To be able to print it simply(without need to use .rawValue), I've added following extensions:

extension Identifier: CustomStringConvertible where Value.RawIdentifier == String {
    var description: String {
        return rawValue
    }
}

extension Identifier where Value.RawIdentifier == Int {
    var description: String {
        return "\(rawValue)"
    }
}

The problem is, it works only for extension which conforms to CustomStringConvertible, and the other one is ignored. And I can't add the conformance to the other extension as they would overlap.

print(Identifier<A>(stringLiteral: "string")) // prints "string"
print(Identifier<B>(integerLiteral: 5)) // prints "Identifier<B>(rawValue: 5)"

Solution

  • You could use a single CustomStringConvertible extension instead of the two you have at the moment, regardless of type:

    extension Identifier: CustomStringConvertible {
        var description: String {
            "\(rawValue)"
        }
    }
    

    For me this correctly prints "string" then "5" as per your last code example.

    This coincidentally is what Sundell does in his open source Identity implementation of Identifiable/Identifier – https://github.com/JohnSundell/Identity/blob/master/Sources/Identity/Identity.swift#L72-L78

    Point-Free's 'Tagged' implementation is also worth a look for reference: https://github.com/pointfreeco/swift-tagged/blob/master/Sources/Tagged/Tagged.swift