I'm seeing some strange behavior at the interface of protocol extensions and generics. I'm new to Swift, so possibly I misunderstand, but I don't see how this can be correct behavior.
First let's define a protocol, and extend it with a default function implementation:
protocol Foo {
}
extension Foo {
static func yo() {
print("Foo.yo")
}
}
Now define a couple of conforming types:
struct A: Foo {
}
struct B: Foo {
static func yo() {
print("B.yo")
}
}
A.yo()
B.yo()
As expected, A.yo()
uses the default implementation of yo
, whereas B.yo()
uses the explicit implementation provided by B
: the output is
Foo.yo
B.yo
Now let's make a simple generic type:
struct C<T: Foo> {
static func what() {
T.yo()
}
}
C<A>.what()
C<B>.what()
C<A>.what()
prints Foo.yo
, as expected. But C<B>.what()
also prints Foo.yo
!
Surely the meaning of C<B>
is simply the template for C
with B
substituted in for the type parameter T
? Yet B
's version of yo
is not being called.
What am I missing? I'm using Swift 5.2.2.
Now, as it turns out you can fix this problem by declaring yo
in the original definition of Foo
. If we do this:
protocol Foo {
static func yo()
}
then C<B>.what()
works as I would expect, printing B.yo
. I can't understand the original behavior in the first place, but even less can I understand how this would change it.
In my actual application I can't use this fix, because I am extending a pre-existing protocol with a function that I want to specialize in a particular conforming type.