---
title: Protocols
source: 'https://academia.sh/en/courses/swift/protocols'
course: 'The Swift Language'
language: en
updated: '2026-10-21T05:03:13+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Defines a protocol as a contract with requirements and a default implementation'
    - 'Measures that a computed property in a protocol extension runs on every call'
    - 'Compares how a protocol type with some and any calls the same requirement'
    - 'Tells apart the moment of two types that fulfill the same requirement with a stored and a computed property'
---

# Protocols

Measures when a value that fulfills a protocol requirement is evaluated: a default implementation runs on every call, but the conforming type decides the moment.

The previous lesson measured properties: a stored property is evaluated on the line where it is defined, a computed property runs again on every read, and a `lazy` property waits until the first read. Now we move to the question of who writes a property. A type can carry a property in order to follow a contract set by another type, and the contract itself can supply a body. How these two cases affect the moment is the subject of this lesson.

A **protocol** is a list of the members a type has to carry: properties, methods, initializers, subscripts and associated types. Each item on the list is a **requirement**. It carries a name, a type and whether it is readable or writable, but it carries no body. A type that conforms to the protocol fulfills the requirement with its own body. In a protocol's **extension**, though, a **default implementation** can be written for a requirement; if the conforming type does not give its own body, the default runs. Extensions get their own treatment in lesson 07; here we use only protocol extensions.

## The Measurement Setup

Every lesson starts with the same helper. `Probe` counts how many times an expression is evaluated and how many times it is used; `moment` derives the moment from those two numbers.

```swift
// Probe.swift — records when an expression is evaluated
final class Probe {
    let label: String
    private(set) var evaluations = 0
    private(set) var uses = 0
    private var evaluatedBeforeUse = false

    init(_ label: String) {
        self.label = label
    }

    func eval<T>(_ value: T) -> T {
        evaluations += 1
        if uses == 0 { evaluatedBeforeUse = true }
        return value
    }

    func use<T>(_ access: () -> T) -> T {
        uses += 1
        return access()
    }

    var moment: String {
        if evaluations == 0 { return "never" }
        if evaluations > 1 { return "on-every-use" }
        return evaluatedBeforeUse ? "immediately" : "on-first-use"
    }

    func report() {
        print("\(label): \(evaluations) evaluations, \(uses) uses -> \(moment)")
    }
}

func tally(_ probes: [Probe]) {
    for moment in ["immediately", "on-first-use", "on-every-use", "never"] {
        let count = probes.filter { $0.moment == moment }.count
        print("\(moment): \(count)")
    }
}
```

**TS21.** Every observation, apart from `never`, carries two uses; the numbers come from two calls on a single type, and because a protocol is not a value itself, what is measured is always an instance of a conforming type.

## A Property in a Protocol Extension

The protocol below sets two requirements: a name and a `Probe`. `greeting`, on the other hand, is a computed property written in the protocol's extension; no conforming type defines it. Two types, `Person` and `Robot`, use the same default implementation.

```swift
// main.swift — a property in a protocol extension runs on every call
protocol Describable {
    var name: String { get }
    var tracker: Probe { get }
}

extension Describable {
    var greeting: String { tracker.eval("hello \(name)") }
}

struct Person: Describable {
    let name: String
    let tracker: Probe
}

struct Robot: Describable {
    let name: String
    let tracker: Probe
}

func makeRobot(_ tracker: Probe) -> some Describable {
    Robot(name: "Rex", tracker: tracker)
}

func twice<T: Describable>(_ value: T) -> Int {
    value.greeting.count + value.greeting.count
}

let direct = Probe("default-implementation")
let existential = Probe("any-protocol-type")
let opaque = Probe("some-protocol-type")
let generic = Probe("generic-function")

let ada = Person(name: "Ada", tracker: direct)
_ = direct.use { ada.greeting }
_ = direct.use { ada.greeting }

let crowd: [any Describable] = [Person(name: "Can", tracker: existential)]
_ = existential.use { crowd[0].greeting }
_ = existential.use { crowd[0].greeting }

let made = makeRobot(opaque)
_ = opaque.use { made.greeting }
_ = opaque.use { made.greeting }

_ = generic.use { twice(Person(name: "Eda", tracker: generic)) }
_ = generic.use { twice(Person(name: "Eda", tracker: generic)) }

let all = [direct, existential, opaque, generic]
for probe in all { probe.report() }
tally(all)
```

```
default-implementation: 2 evaluations, 2 uses -> on-every-use
any-protocol-type: 2 evaluations, 2 uses -> on-every-use
some-protocol-type: 2 evaluations, 2 uses -> on-every-use
generic-function: 4 evaluations, 2 uses -> on-every-use
immediately: 0
on-first-use: 0
on-every-use: 4
never: 0
```

All four observations came out as **on every use**. A default implementation is a computed property: the protocol does not store it, and it runs the body again on every read. The three ways of writing a protocol type gave the same result. `any Describable` creates an **existential type**: the real type of the value is stored at runtime and the call is forwarded to it. `some Describable` gives an **opaque type**, which hides the type but keeps it fixed at compile time. The `T: Describable` constraint of a generic function (detailed in lesson 08) reaches the same requirement as well. In all three, `greeting` was computed again on each call; because `twice` reads it twice in one call, the evaluation count in that observation rose to four. In M13/K01 we saw that the common interface `Enumerable` supplies bodies; in Ruby, which body answers from that interface is a separate question. We do not ask it here, we ask only when the body runs. **TS22.** In the measurement no conforming type rewrote the default with its own body; a type giving its own body stayed outside this lesson.

For standard protocols such as `Equatable`, even the compiler can write the default. It is enough for a structure whose stored properties are all equatable to say that it conforms to `Equatable`; the compiler derives the comparison operator.

```swift
// main.swift — Equatable derivation
struct Point: Equatable {
    let x: Int
    let y: Int
}

print(Point(x: 1, y: 2) == Point(x: 1, y: 2))
print(Point(x: 1, y: 2) == Point(x: 2, y: 1))
```

```
true
false
```

We did not write the equality operator; looking at the type definition, the compiler produced a body that compares each field in order. This derivation is a decision made at compile time, not something we measure at run time.

## The Customization Point

The default implementation in a protocol extension has a subtlety: not every member written in the extension is the same. A member that is also counted among the protocol's requirements becomes a **customization point**. When the conforming type gives its own body, a call made through the protocol type goes to that body too. A member that is not in the requirement list and is written only in the extension is not a customization point: when it is called through the protocol type, the body in the extension runs, even if the conforming type has a member of the same name.

We watch this with two `Probe`s. `standardProbe` counts the default body in the extension, and `customProbe` counts the conforming type's own body. `title` is a requirement of the protocol; `badge` is written only in the extension. `Book` rewrites both with its own body.

```swift
// main.swift — the difference between a requirement and an extension-only member
protocol Badged {
    var standardProbe: Probe { get }
    var customProbe: Probe { get }
    var title: String { get }
}

extension Badged {
    var title: String { standardProbe.eval("default title") }
    var badge: String { standardProbe.eval("default badge") }
}

struct Book: Badged {
    let standardProbe: Probe
    let customProbe: Probe
    var title: String { customProbe.eval("book title") }
    var badge: String { customProbe.eval("book badge") }
}

func probes(_ label: String) -> (standard: Probe, custom: Probe) {
    (Probe("\(label)/extension"), Probe("\(label)/type"))
}

let titleViaProtocol = probes("title-via-protocol")
let badgeViaProtocol = probes("badge-via-protocol")
let badgeViaType = probes("badge-via-type")

let titled: any Badged = Book(standardProbe: titleViaProtocol.standard, customProbe: titleViaProtocol.custom)
_ = titleViaProtocol.custom.use { titled.title }
_ = titleViaProtocol.custom.use { titled.title }

let badged: any Badged = Book(standardProbe: badgeViaProtocol.standard, customProbe: badgeViaProtocol.custom)
_ = badgeViaProtocol.standard.use { badged.badge }
_ = badgeViaProtocol.standard.use { badged.badge }

let book = Book(standardProbe: badgeViaType.standard, customProbe: badgeViaType.custom)
_ = badgeViaType.custom.use { book.badge }
_ = badgeViaType.custom.use { book.badge }

let all = [titleViaProtocol.standard, titleViaProtocol.custom, badgeViaProtocol.standard, badgeViaProtocol.custom, badgeViaType.standard, badgeViaType.custom]
for probe in all { probe.report() }
```

```
title-via-protocol/extension: 0 evaluations, 0 uses -> never
title-via-protocol/type: 2 evaluations, 2 uses -> on-every-use
badge-via-protocol/extension: 2 evaluations, 2 uses -> on-every-use
badge-via-protocol/type: 0 evaluations, 0 uses -> never
badge-via-type/extension: 0 evaluations, 0 uses -> never
badge-via-type/type: 2 evaluations, 2 uses -> on-every-use
```

When `title`, a requirement, was called through the protocol type, `Book`'s own body ran; the default body in the extension stayed `never`. `badge`, which is not a requirement, ran the body in the extension when called through the same `any Badged` value, and `Book`'s own `badge` body stayed `never`. When the same `badge` was called through the concrete `Book` type, the reverse happened: the type's own body ran. This difference shows that writing a member in the requirement list is not only a declaration; it decides which body will run. If you want the types to be able to customize a member added to an extension, you first write it into the protocol's requirements. **TS23.** This difference was measured for structure types and for the `any` protocol type; classes and values returned with `some` were not tried separately.

## Protocol Composition

If a value has to conform to more than one protocol, we join the types with `&`: `some Named & Priced`. A **protocol composition** does not define a new protocol; it only asks, for the type of a parameter or a return value, for several requirements together. To measure whether a composition changes the moment of the requirements, we use a type that fulfills the two requirements in different ways: `name` is stored, `price` is computed.

```swift
// main.swift — a protocol composition does not change the moment of the requirements
protocol Named { var name: String { get } }
protocol Priced { var price: Int { get } }

struct Product: Named, Priced {
    let name: String
    let items: [Int]
    let tracker: Probe
    var price: Int { tracker.eval(items.reduce(0, +)) }
}

func label(_ value: some Named & Priced) -> String {
    "\(value.name): \(value.price)"
}

let nameProbe = Probe("composition-name")
let priceProbe = Probe("composition-price")

let product = Product(name: nameProbe.eval("Notebook"), items: [3, 4], tracker: priceProbe)
_ = nameProbe.use { label(product) }
_ = nameProbe.use { label(product) }
_ = priceProbe.use { label(product) }

let pair = [nameProbe, priceProbe]
for probe in pair { probe.report() }
```

```
composition-name: 1 evaluations, 2 uses -> immediately
composition-price: 3 evaluations, 1 uses -> on-every-use
```

A composition is only a boundary: the `label` function asks for the requirements of both protocols together, but the moment of each requirement comes from the conforming type's declaration. The stored `name` was evaluated once, when `Product` was built: **immediately**. The computed `price`, on the other hand, ran again each time `label` was called. Of the three calls to `label`, two were counted with `nameProbe` and one with `priceProbe`, so `priceProbe` shows three evaluations in total; this shows which calls the counter passed through and does not indicate a separate moment. Even though `priceProbe` carries a single use, it received three evaluations, so the rule counts it as **on every use** without falling into the ambiguity of a single-use observation. **TS24.** The composition was measured here as a function parameter and with `some`; the `any Named & Priced` spelling was not tried separately.

## A Constrained Protocol Extension

A `where` condition can be added to a protocol extension: `extension Ranked where Self: Comparable`. The members in such an extension are visible only on types that meet the condition. If the same name is written in both the general extension and the constrained extension, the more specific one, that is the constrained extension, is chosen for a type that meets the condition; a type that does not meet it falls back to the general one. We see which body runs with two `Probe`s: `generalProbe` counts the general extension and `specificProbe` counts the constrained one.

```swift
// main.swift — the constrained extension is chosen when it is more specific
protocol Ranked {
    var points: Int { get }
    var generalProbe: Probe { get }
    var specificProbe: Probe { get }
}

extension Ranked {
    var rank: String { generalProbe.eval("unranked") }
}

extension Ranked where Self: Comparable {
    var rank: String { specificProbe.eval("ranked") }
}

struct Score: Ranked, Comparable {
    let points: Int
    let generalProbe: Probe
    let specificProbe: Probe
    static func < (left: Score, right: Score) -> Bool { left.points < right.points }
    static func == (left: Score, right: Score) -> Bool { left.points == right.points }
}

struct Plain: Ranked {
    let points: Int
    let generalProbe: Probe
    let specificProbe: Probe
}

let comparableGeneral = Probe("comparable/general")
let comparableSpecific = Probe("comparable/constrained")
let plainGeneral = Probe("plain/general")
let plainSpecific = Probe("plain/constrained")

let score = Score(points: 3, generalProbe: comparableGeneral, specificProbe: comparableSpecific)
_ = comparableSpecific.use { score.rank }
_ = comparableSpecific.use { score.rank }

let plain = Plain(points: 3, generalProbe: plainGeneral, specificProbe: plainSpecific)
_ = plainGeneral.use { plain.rank }
_ = plainGeneral.use { plain.rank }

let all = [comparableGeneral, comparableSpecific, plainGeneral, plainSpecific]
for probe in all { probe.report() }
tally(all)
```

```
comparable/general: 0 evaluations, 0 uses -> never
comparable/constrained: 2 evaluations, 2 uses -> on-every-use
plain/general: 2 evaluations, 2 uses -> on-every-use
plain/constrained: 0 evaluations, 0 uses -> never
immediately: 0
on-first-use: 0
on-every-use: 2
never: 2
```

Because `Score` is both `Comparable` and conforms to `Ranked`, the `rank` call went to the body in the constrained extension; the general body stayed `never`. `Plain` is not `Comparable`, so the constrained extension was ignored for it and the general body ran on every call. In both cases the body that ran was evaluated again on every use: the constraint decides which body is chosen, not when it runs. This choice is made at compile time, when the concrete type is known; calling a constrained extension through a protocol value is a separate question and was not measured here. **TS25.** The choice was measured only for calls made through a concrete type.

## The Bounding Measurement

The protocol does not decide the moment itself: the type that fulfills the requirement does. The `Totaled` protocol below sets a single requirement: `var total: Int { get }`. `StoredTotal` fulfills it with a stored `let`, and `ComputedTotal` with a computed property. Both types are read from the same function; the code that reads cannot see the difference between them.

```swift
// main.swift — the same requirement, two conformances, two moments
protocol Totaled {
    var total: Int { get }
}

struct StoredTotal: Totaled {
    let total: Int
}

struct ComputedTotal: Totaled {
    let items: [Int]
    let tracker: Probe
    var total: Int { tracker.eval(items.reduce(0, +)) }
}

func read<T: Totaled>(_ value: T) -> Int { value.total }

let storedProbe = Probe("stored-conformance")
let computedProbe = Probe("computed-conformance")

let fixedTotal = StoredTotal(total: storedProbe.eval(9))
let derivedTotal = ComputedTotal(items: [4, 5], tracker: computedProbe)

_ = storedProbe.use { read(fixedTotal) }
_ = storedProbe.use { read(fixedTotal) }
_ = computedProbe.use { read(derivedTotal) }
_ = computedProbe.use { read(derivedTotal) }

let pair = [storedProbe, computedProbe]
for probe in pair { probe.report() }
```

```
stored-conformance: 1 evaluations, 2 uses -> immediately
computed-conformance: 2 evaluations, 2 uses -> on-every-use
```

The same `total` requirement, two different moments: the stored conformance is **immediately**, the computed conformance is **on every use**. The protocol does not see this difference; it only says that an `Int` will be returned. So when you read a protocol requirement, you cannot work out the cost of the expression from the protocol's name; you have to look at the declaration of the conforming type. **TS26.** In this measurement the value of `StoredTotal` was built with an expression wrapped in a `Probe`; where a stored value comes from in a real program is a separate question and was not measured here.

## Summary

- A protocol is a contract that lists requirements; the default implementation written in its extension supplies a body to types that do not fulfill the requirement themselves.
- A computed property in a protocol extension runs again on every read; `any`, `some` and a generic constraint did not change this behavior.
- The compiler derives the implementation from the stored properties for some protocols such as `Equatable`.
- If a stored property fulfills the same requirement, the measurement is **immediately**; if a computed property does, it is **on every use**: the moment is decided by the conforming type, not the protocol.
- This lesson's moment: **on every use** (the default implementation in a protocol extension).

## Next Step

A protocol extension can add a body to a protocol whose definition belongs to someone else. But does this extension mechanism work only with protocols? The next lesson measures adding methods and properties from the outside to standard types such as `Int` and `String`, and what cannot be added.
