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 theEquatableprotocol;sorted()sorts onlyComparablevalues;printlooks forCustomStringConvertibleon a value to learn how to show it;Setand dictionary keys accept onlyHashablevalues.
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:
error: binary operator '==' cannot be applied to two 'Point' operandsEquatable: are two values equal
Let's put the Equatable badge on the point:
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 trueThe 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 toEquatable.===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:
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)) // trueLet's look at what happens here:
Comparablerequires the<operator. Swift will not write it for you: it does not know which property you want to compare by.- The operator is declared as a type method,
static func, only instead of a name you write the<sign itself. - The standard library derives the operators
>,<=and>=from<, and Swift writes==itself: the requirements ofEquatableare part ofComparable. - 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:
extension Score: CustomStringConvertible {
var description: String { "\(points) pts" }
}
print(ranked[0]) // 3 pts
print("best: \(ranked[2])") // best: 7 ptsThe 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:
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:
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:
==needsEquatable,sorted()needsComparable, aSetand dictionary keys needHashable. - A struct whose stored properties are all
EquatableorHashableonly has to declare that badge: Swift writes the rest itself.Comparableneeds your own<operator. printand interpolation take the text fromdescriptiononly if the type declaredCustomStringConvertible.==compares values and requiresEquatable, while===checks whether it is one class object and requires no protocol.