Calling a protocol with associatedType's method, doesn't always pick the same specialized generic implementation

This understanding (specifically, "Hence") is incorrect. The "most specific" implementation of baz() that is called when using a value of concrete type, whether ExplicitFoo or GenericFoo<Int>, is determined without regard to whether any implementation satisfies a protocol conformance.

In Swift, a type can conform to a protocol in exactly one way; a generic type such as GenericFoo cannot conform to a protocol in different ways depending on its generic parameter(s).

For ExplicitFoo, the "most specific" implementation of baz() which satisfies the protocol requirement is this one:

extension Foo<Int> {
    func baz() -> String { "Foo<Int>" } /* (b) */
}

By contrast, for GenericFoo, the "most specific" implementation of baz() which satisfies the protocol requirement is this one:

extension Foo {
    func baz() -> String { "Foo" } /* (a) */
}

When you are using a value of concrete type GenericFoo<Int>, then there is an additional implementation of baz() given by (b) which shadows but does not override the implementation that satisfies the protocol requirement, which is still (a). This goes back to the principle that there is one and only one way in which GenericFoo conforms to Foo.

When not using the concrete type, you are explicitly requiring the compiler to dynamically dispatch to the implementation of baz() which satisfies the protocol requirement. For any value of type GenericFoo, that would be implementation (a).

2 Likes