How does Swift choose a method from candidates in multiple conditional conformances

Take Dictionary as an example, suppose I define multiple conditional conformances for it, each having a method with same signature, which one will the compiler choose when I call that method on a dict which has all conformances?

I have run into this scenario a few times. At first I just changed func name to avoid it. But I also did experiments and found compiler behaved as same as my intuition. It looks for the most specific condition. If found, it uses that method; otherwise, it throws ambiguity error.

Setup
struct Foo: Identifiable, Comparable, Hashable {
    var id: UUID = UUID()
    var value: Int = 0

    static func < (lhs: Foo, rhs: Foo) -> Bool {
        lhs.value < rhs.value
    }
}
Example 1: there is a most specific condition
extension Dictionary where Value: Identifiable {
    func get(id: Key) -> Element? {
        print("Identifiable version is called")
        return nil
    }
}

extension Dictionary where Value: Identifiable, Value: Comparable {
    func get(id: Key) -> Element? {
        print("Identifiable&Comparable version is called")
        return nil
    }
}

func test1() {
    let foo1 = Foo()
    let foo2 = Foo()
    let foos = [foo1.id: foo1, foo2.id: foo2]
    foos.get(id: UUID())
}
Example 2: there isn't one

extension Dictionary where Value: Identifiable, Value: Hashable {
    func get(id: Key) -> Element? {
        print("Identifiable&Hashable version is called")
        return nil
    }
}

extension Dictionary where Value: Identifiable, Value: Comparable {
    func get(id: Key) -> Element? {
        print("Identifiable&Comparable version is called")
        return nil
    }
}

func test2() {
    let foo1 = Foo()
    let foo2 = Foo()
    let foos = [foo1.id: foo1, foo2.id: foo2]
    foos.get(id: UUID())
}

Does anyone know if this is an officially supported feature? I ask because I can't find any mention of this behavior on the net.

EDIT: Another question. What's the recommended solution in example 2 above? Is it possible to give hints to compiler somehow other than changing func name?

I should have checked SE-0143 first.

When satisfying a protocol requirement, Swift chooses the most specific member that can be used given the constraints of the conformance.

2 Likes

It's worth noting that your examples are not conditional conformances because your extensions don't introduce new protocol conformances. So SE-0143 doesn't apply.

I don't know if the community has an official term for extensions of the kind:

extension Dictionary where Value: Identifiable

I guess I'd call this a constrained extension or conditional extension.

But the rules are similar to those stated in SE-0143. Given multiple overloads of the same symbol, the type checker will generally pick the most specific one that matches, or raise an error when there's ambiguity. I don't think the concrete rules are documented in a single place.

You may be able to add @_disfavoredOverload to one of the methods, but it's not an official feature.

1 Like