What must Hashable guarantee, and why does iteration order change between launches?
Predict first
How many elements does the set hold?
struct User: Hashable {
let id: Int
let name: String
static func == (a: User, b: User) -> Bool { a.id == b.id }
func hash(into h: inout Hasher) { h.combine(id) }
}
print(Set([User(id: 1, name: "A"), User(id: 1, name: "B")]).count)
- 1
- 2
Predict first
What does this print?
var counts: [Character: Int] = [:]
for c in "hello" { counts[c, default: 0] += 1 }
print(counts["l"]!)
- 1
- 2
- Crash
Mental model. A hash is a coat-check number. Two identical coats must get the same number, but two different coats may share one.
The contract is one rule: equal values must produce the same hash. The reverse is allowed (different values can collide). If == and hash(into:) disagree, a value can sit in a Set or Dictionary and still not be found.
Swift seeds its hash function randomly for every process, so the same set iterates in a different order each launch. That is deliberate: it stops code depending on order and blunts hash-flooding attacks. Never persist hashValue. For reproducible debugging or tests, set the SWIFT_DETERMINISTIC_HASHING environment variable.
== and the hash both look only at id, so the two users are equal and hash alike. If the hash also combined name, the contract would break.
The default: subscript reads 0 for a missing key and writes back after the increment, so you never unwrap or branch.
Common traps
- Hashing a field that
==ignores. Equal values then land in different buckets and a lookup misses. - Mutating a key after inserting it. With a class key, changing a hashed field strands the entry. Hash only immutable state.
They might follow up with
- How do you implement Hashable by hand? Feed the same fields
==compares intohasher.combine, inhash(into:). - Why not store
hashValuein a database? It changes per process, so yesterday’s numbers mean nothing today.
Related questions
- 2.1.1 Array, Set or Dictionary: how do you choose, and what does each operation cost?
- 2.9.5 How does equality work for NSObject subclasses, and why do two “equal” objects stay distinct in a Set?
← Array, Set or Dictionary: how do you choose, and what does each… When is the standard library not enough, and what does… →