Search code examples
swiftgenericsoverload-resolution

Swift cannot find overload when generics involved


I have the following code:

protocol MyEnum {

}

class MyClass {
    func append(_ value: UInt8) {
        print("uint8 \(value)")
    }

    func append(_ value: UInt16) {
        print("uint16 \(value)")
    }

    func append<T: MyEnum>(_ value: T) {
        print("myenum \(value)")
    }
}

let c = MyClass()

func appendAny<T>(_ value: T) {
    switch value {
    case let u8 as UInt8:
        c.append(u8)
    case let u16 as UInt16:
        c.append(u16)
    case let myenum as MyEnum:
        c.append(myenum)
    default:
        assert(false)
    }
}

The error is reported by the compiler for the line c.append(myenum) stating

Cannot invoke 'append' with an argument list of type (MyEnum)

Please, explain to me why this happens and what is the correct solution in this case. I want to preserve my overloads, since when called with a concrete MyEnum instance, all works fine.


Solution

  • As Martin noted in his comment, you should change your final append() overload to be:

    func append(_ value: MyEnum) {
        print("myenum \(value)")
    }
    

    Because you want to treat the value as "an instance of a protocol", not an "instance of a concrete type that happens to conform to the protocol". An instance that has a protocol type is not also a concrete type that conforms to that protocol.

    Another note: since you aren't really using generic constraints or any compile-time properties of the type T (only runtime casting), why not just change appendAny to be:

    func appendAny(_ value: Any) {
        switch value {
        case let u8 as UInt8:
            c.append(u8)
        case let u16 as UInt16:
            c.append(u16)
        case let myenum as MyEnum:
            c.append(myenum)
        default:
            assert(false)
        }
    }