Default implementations
A second task was added to the "Shape" job description: describe yourself in one line. Almost every shape will answer the same way, by stating its area. Writing the same line in every type is dull, and any copy can go wrong.
So a standard instruction is attached to the job description: "If you have no way of your own, do it like this." Whoever knows better works their own way. Everyone else takes the instruction and writes nothing.
In Swift such an instruction is a method in a protocol extension. It is called a default implementation: a type that has not written its own method gets this one.
A method from a protocol extension
You can extend not only types but protocols themselves. Let's describe the second task and give it a standard instruction:
protocol Shape {
func area() -> Double
func describe() -> String
}
extension Shape {
func describe() -> String { "area \(area())" }
}
struct Square: Shape {
let side: Double
func area() -> Double { side * side }
}
print(Square(side: 2).describe()) // area 4.0Let's go through the program part by part:
- The protocol has two requirements:
area()anddescribe(). extension Shapeextends the protocol itself. The methoddescribe()in this extension is the default implementation.- Inside, it calls
area(). The instruction knows nothing about the square's side, but it knows that every shape hasarea(). Squarewrites onlyarea(). The square getsdescribe()from the extension, and the compiler counts both requirements as met.
Here the instruction picture is inaccurate. A real employee can read the instruction and adjust something in it for themselves. A default implementation is the same for everyone and uses only what the protocol requires: it knows nothing about side or radius.
A type's own method instead of the standard one
A type that has its own way writes the method itself. Then it does not need the standard instruction:
struct Circle: Shape {
let radius: Double
func area() -> Double { 3.14 * radius * radius }
func describe() -> String { "circle, radius \(radius)" }
}
let shapes: [any Shape] = [Square(side: 2), Circle(radius: 1)]
for shape in shapes {
print(shape.describe(), terminator: "; ") // area 4.0; circle, radius 1.0;
}The square described itself by the instruction, and the circle in its own words. A type's own method replaces the default implementation. This works through any Shape too. describe() is on the list of requirements, so Swift finds the method of the type whose value is in shape, as with area() in the theme's first lesson.
Common mistake
A method was added only to the protocol extension and not to the list of requirements. The circle wrote its own version, and you expect it to be called:
extension Shape {
func summary() -> String { "a shape" }
}
extension Circle {
func summary() -> String { "a circle" }
}
print(Circle(radius: 1).summary()) // a circle
for shape in shapes {
print(shape.summary(), terminator: "; ") // ✗
}The program compiles, but the loop prints a shape; a shape;, for the square and for the circle alike. summary() is not among the protocol's requirements. About a value of type any Shape, Swift knows only that it has the protocol's requirements and the methods from the protocol's extension. So the extension's version is called, even when shape holds a circle. A direct call, Circle(radius: 1).summary(), still gives a circle.
Interviewers love this case: which method is called through any Shape if the protocol does not list it? The answer is the method from the protocol extension. The fix is to add func summary() -> String to the protocol next to describe(). Then calls through any Shape reach the circle's method.
Key points
- A method in a protocol extension is a default implementation: every type that has not written its own gets it.
- A type's own method replaces the default implementation, and it is the one called through
any Shapewhen the method is among the requirements. - A default implementation uses only what the protocol requires.
- A method that is not among the requirements is always taken from the protocol extension when called through
any Shape.