Swift allows you to define extension methods on any type or protocol. While extension methods on protocols are frequently used to provide default implementations, only methods listed in the main protocol body can go in the vtable, since you can define an extension method from anywhere. Consider this code:
// module A
public protocol AProtocol {
func getSomeNumber() -> Int
}
public struct ExampleImplementation: AProtocol {
public func getSomeNumber() -> Int { 4 }
}
// module B
import A
extension AProtocol {
public func getSomeText() -> String {
getSomeNumber().description
}
}
public func printThing(_ x: some AProtocol) {
print(x.getSomeText())
}
// module C
import A
import B
struct ActualImplementation: AProtocol {
func getSomeNumber() -> Int { 5 }
func getSomeText() -> String { "five" }
}
let x = ActualImplementation()
print(x.getSomeText()) // static lookup finds ActualImplementation.getSomeText and prints "five"
printThing(x) // since getSomeText isn't defined in module A, it's not in the vtable
// so this uses the implementation from the extension and prints "5"
C#'s extension methods are just syntax sugar for static method calls and are resolved at compile time, so the equivalent code would also demonstrate this behavior in C#. Both Swift and C# have a concept of ABI stability and allow libraries to be distributed in binary form, so compile-time templates are a non-starter for these languages.
How could extension methods be dynamically dispatched in a language that allows binary dependencies?
getSomeText(), even when module A and/or module B are binary dependencies — which is something Swift and C# can’t do. $\endgroup$getSomeText()actually dispatched dynamically? It seems since it's implemented in terms of the underlying interface that it would be dispatched statically and only the call togetSomeNumber()would be dynamic. Or could it be overridden by other implementations of the protocol? $\endgroup$