Skip to content
Theory · Protocols and generics · lesson 6 of 9

Standard protocols

Your program is not the only one posting job openings. The Swift standard library has posted its own in advance, and its operators and functions look at badges:

  • == compares only values with the Equatable protocol;
  • sorted() sorts only Comparable values;
  • print looks for CustomStringConvertible on a value to learn how to show it;
  • Set and dictionary keys accept only Hashable values.

Without a badge, even comparing two points does not compile. If you declare struct Point without a protocol, the line a == b gives an error:

TEXT
error: binary operator '==' cannot be applied to two 'Point' operands

Equatable: are two values equal

Let's put the Equatable badge on the point:

SWIFT
struct Point: Equatable {
    let x: Int
    let y: Int
}

let a = Point(x: 1, y: 2)
let b = Point(x: 1, y: 2)
print(a == b, a != Point(x: 2, y: 1)) // true true

The requirement of Equatable is the == operator. You did not have to write it: Swift wrote it itself, because both of the point's properties are Int, and it knows how to compare numbers. Two points are equal when all their properties are equal. The != operator comes along with ==.

Swift writes == itself for structs and enums. A class has to write == itself: class Ticket: Equatable without its own == does not compile.

Here the job-opening picture is inaccurate. For an ordinary job the candidate prepares on their own, while for Equatable Swift often prepares the struct: putting on the badge is enough.

== and ===

Interviewers like to ask this: how does == differ from ===?

  • == compares values and requires the type to conform to Equatable.
  • === from the classes theme checks whether two references lead to one and the same object. It needs no protocol, but it works only with classes.

Two tickets Ticket(seat: 5) created separately are equal by == if the class was given an == that compares seat numbers. But === between them gives false: there are two objects.

Comparable: which is smaller

For values to be sorted, the type must be able to say which of two values is smaller:

SWIFT
struct Score: Comparable {
    let points: Int
    static func < (a: Score, b: Score) -> Bool {
        a.points < b.points
    }
}

let scores = [Score(points: 7), Score(points: 3), Score(points: 5)]
let ranked = scores.sorted()
print(ranked[0].points, ranked[2].points) // 3 7
print(Score(points: 2) < Score(points: 5)) // true

Let's look at what happens here:

  1. Comparable requires the < operator. Swift will not write it for you: it does not know which property you want to compare by.
  2. The operator is declared as a type method, static func, only instead of a name you write the < sign itself.
  3. The standard library derives the operators >, <= and >= from <, and Swift writes == itself: the requirements of Equatable are part of Comparable.
  4. Now sorted() puts the scores in order from the smallest to the largest.

CustomStringConvertible: how to print

Let's put one more badge on the score, this time with an extension:

SWIFT
extension Score: CustomStringConvertible {
    var description: String { "\(points) pts" }
}

print(ranked[0]) // 3 pts
print("best: \(ranked[2])") // best: 7 pts

The protocol's requirement is a property description that can be read. print and string interpolation take the text from it. Here description is a computed property, so the text is built again on every print and shows the current values. Without the badge, print would show the type and its fields: Score(points: 3).

Hashable: for sets and dictionaries

The elements of a Set and the keys of a dictionary, as you already know, must be Hashable. You put on the Hashable badge the same way as Equatable: if all stored properties are Hashable, Swift writes what is needed itself. After the line extension Point: Hashable {} points can go into a set, and Set([a, b]) holds one point, because a == b.

Without the badge, a set does not accept the point:

TEXT
error: generic struct 'Set' requires that 'Point' conform to 'Hashable'

Common mistake

The description property was written, but the conformance to the protocol was not declared:

SWIFT
struct Day {
    var n: Int
    var description: String { "day \(n)" }
}

print(Day(n: 4)) // ✗

The program compiles, but it prints Day(n: 4), not day 4. A property with the right name is not enough: print takes description only from a type with the CustomStringConvertible badge. The fix is struct Day: CustomStringConvertible.

Key points

  • Standard operators and functions require badges: == needs Equatable, sorted() needs Comparable, a Set and dictionary keys need Hashable.
  • A struct whose stored properties are all Equatable or Hashable only has to declare that badge: Swift writes the rest itself. Comparable needs your own < operator.
  • print and interpolation take the text from description only if the type declared CustomStringConvertible.
  • == compares values and requires Equatable, while === checks whether it is one class object and requires no protocol.