swift

Protocol extension has no impact on matching class


Here is a code snippet to narrow down the problem:

import Foundation

protocol SampleProtocol {
    func foo()
}

extension SampleProtocol {
    func foo() {
        print("protocol foo")
    }
    
    func bar() {
        print("protocol bar")
    }
}

class SampleClass: SampleProtocol {
    func foo() {
        print("class foo")
    }
    
    func bar() {
        print("class bar")
    }
}

let sample: SampleProtocol = SampleClass()  
sample.foo()
sample.bar()

The output is

class foo
protocol bar

But why does it work this way? Why does protocol extension have no impact on SampleClass ? My expectation was to have

class foo
class bar 

output


Solution

  • Despite being syntactically similar, foo and bar in the protocol extension are semantically very different.

    Since the signature of foo in the extension matches the protocol requirement foo, it is a default implementation of foo. When you call sample.foo(), the default implementation is not called because SampleClass has its own implementation of foo.

    On the other hand, bar in the extension does not match any protocol requirements, so it is just a plain old regular method you added to the protocol. It is also not a protocol requirement, which can only be declared inside the protocol declaration itself. It is similar to a regular old global function like this:

    func bar(_ `self`: any SampleProtocol) {
        print("protocol bar")
    }
    

    SampleClass.bar is totally unrelated to the bar in the protocol extension. Naturally, sample.bar() resolves to the bar in the protocol extension, not SampleClass.bar, because sample of type SampleProtocol at compile time.