0
0
Swiftprogramming~15 mins

Equatable, Hashable, Comparable protocols in Swift - Deep Dive

Choose your learning style9 modes available
Overview - Equatable, Hashable, Comparable protocols
What is it?
In Swift, Equatable, Hashable, and Comparable are special protocols that let you compare and organize your data easily. Equatable lets you check if two values are the same. Hashable allows your data to be stored in collections like sets or used as dictionary keys by providing a unique identifier. Comparable lets you order values, like sorting numbers or names. These protocols help your program understand how to compare and manage your custom types.
Why it matters
Without these protocols, you would have to write a lot of repetitive code to compare or sort your data, which is slow and error-prone. They make your code cleaner and faster by letting Swift handle comparisons and collections efficiently. Imagine trying to find a friend in a phone book without knowing how to compare names or sort them—it would be very hard. These protocols solve that problem for your data.
Where it fits
Before learning these protocols, you should understand basic Swift types and how to write simple functions. After mastering them, you can learn about advanced collection types, custom sorting, and performance optimization in Swift programs.
Mental Model
Core Idea
These protocols define how to check equality, create unique identifiers, and order values so Swift can compare and organize your data automatically.
Think of it like...
Think of Equatable as a way to check if two keys open the same door, Hashable as giving each key a unique code so it can be quickly found in a big keychain, and Comparable as knowing which key is bigger or smaller to arrange them neatly.
┌───────────────┐      ┌───────────────┐      ┌───────────────┐
│   Equatable   │      │   Hashable    │      │  Comparable   │
│  (==, !=)    │      │  (hashValue)  │      │  (<, <=, >, >=)│
└──────┬────────┘      └──────┬────────┘      └──────┬────────┘
       │                      │                      │
       │                      │                      │
       ▼                      ▼                      ▼
  Check if two          Provide unique          Order values
  values are equal      identifier for          to sort or
                        collections             compare
Build-Up - 7 Steps
1
FoundationUnderstanding Equatable Basics
🤔
Concept: Learn what Equatable means and how to use it to check if two values are equal.
Equatable is a protocol that requires you to define how two instances of a type are compared for equality using the == operator. Swift can then use this to check if two values are the same. For example, Int and String already conform to Equatable, so you can write: let a = 5; let b = 5; print(a == b) // true. You can make your own types Equatable by implementing the == function.
Result
You can compare your custom types using == and != operators.
Understanding Equatable is the first step to letting Swift compare your custom data types easily and consistently.
2
FoundationIntroducing Hashable Protocol
🤔
Concept: Learn how Hashable lets your data be stored in sets or used as dictionary keys by providing a unique hash value.
Hashable builds on Equatable and requires a hash(into:) method that feeds your properties into a hasher. This hash value helps Swift quickly find your data in collections like Set or Dictionary. For example, String and Int conform to Hashable. To make your own type Hashable, you must provide a hash(into:) method that feeds your properties into a hasher.
Result
Your custom types can be used in sets and as dictionary keys.
Knowing Hashable lets you use your types in powerful Swift collections that rely on fast lookups.
3
IntermediateMaking Custom Types Equatable
🤔Before reading on: do you think Swift can automatically make your struct Equatable if all its properties are Equatable? Commit to your answer.
Concept: Swift can automatically generate Equatable conformance for structs with Equatable properties.
If all properties of your struct conform to Equatable, you can simply declare conformance without writing == yourself. For example: struct Point: Equatable { var x: Int; var y: Int } Swift will generate == that compares x and y automatically. This saves time and reduces errors.
Result
Your struct can be compared with == without extra code.
Understanding automatic synthesis of Equatable saves you from writing boilerplate and ensures consistent equality checks.
4
IntermediateImplementing Hashable for Custom Types
🤔Before reading on: do you think you must manually combine all properties to create a hash value, or does Swift help you? Commit to your answer.
Concept: Swift can automatically synthesize Hashable conformance for structs with Hashable properties, but you can customize hash(into:) if needed.
Like Equatable, if all properties conform to Hashable, you can declare Hashable conformance and Swift generates hash(into:) for you. For example: struct Point: Hashable { var x: Int; var y: Int } Swift combines the hashes of x and y automatically. You can override hash(into:) to customize hashing if needed.
Result
Your struct can be used in sets and dictionaries without manual hashing code.
Knowing when Swift synthesizes Hashable helps you write less code and maintain performance.
5
IntermediateUnderstanding Comparable Protocol
🤔Before reading on: do you think Comparable requires only one operator or multiple to define ordering? Commit to your answer.
Concept: Comparable requires you to define the < operator, and Swift provides the rest of the comparison operators automatically.
Comparable lets you order values by defining the < operator. Once you implement <, Swift gives you <=, >, and >= for free. For example: struct Person: Comparable { var age: Int; static func < (lhs: Person, rhs: Person) -> Bool { lhs.age < rhs.age } } This lets you sort arrays of Person by age.
Result
You can compare and sort your custom types easily.
Understanding that only < is needed simplifies implementing Comparable and enables full ordering.
6
AdvancedCombining Protocols for Powerful Types
🤔Before reading on: do you think a type can conform to Equatable, Hashable, and Comparable all at once? Commit to your answer.
Concept: Types often conform to all three protocols to be fully usable in collections and sorting operations.
A common pattern is to make your type conform to Equatable, Hashable, and Comparable together. This allows equality checks, hashing for collections, and ordering for sorting. For example: struct Employee: Equatable, Hashable, Comparable { var id: Int; var name: String; static func < (lhs: Employee, rhs: Employee) -> Bool { lhs.name < rhs.name } } Swift can synthesize Equatable and Hashable if properties conform, and you implement < for Comparable.
Result
Your type is versatile and works well with Swift's standard library features.
Knowing how to combine these protocols unlocks full power for your custom types in real-world apps.
7
ExpertCustomizing Hashing and Equality for Performance
🤔Before reading on: do you think using all properties for hashing and equality is always best? Commit to your answer.
Concept: Sometimes customizing which properties participate in hashing and equality improves performance and correctness.
By default, all properties are used for Equatable and Hashable synthesis. But you can override == and hash(into:) to use only important properties. For example, if your type has a unique ID, you might compare and hash only that. This reduces computation and avoids bugs when some properties are irrelevant for identity. Example: struct User: Hashable { var id: Int; var name: String; static func == (lhs: User, rhs: User) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } }
Result
Your type compares and hashes efficiently and correctly in complex scenarios.
Understanding when and how to customize hashing and equality prevents subtle bugs and improves app speed.
Under the Hood
When you use Equatable, Swift calls your == function to compare two values property by property. Hashable uses a hashing algorithm that combines the hashes of each property into a single integer, which helps collections find your data quickly. Comparable uses the < operator you define to decide order. Swift's compiler can automatically generate code for these protocols when all properties conform, saving you from writing repetitive code.
Why designed this way?
Swift protocols like Equatable, Hashable, and Comparable were designed to make data comparison and organization simple and consistent. Automatic synthesis reduces boilerplate and errors. The separation into three protocols allows flexibility: you can choose just equality, or add hashing for collections, or ordering for sorting. This modular design fits many use cases and keeps Swift code clean and efficient.
┌───────────────┐      ┌───────────────┐      ┌───────────────┐
│   Equatable   │      │   Hashable    │      │  Comparable   │
│  func ==()   │      │ func hash(into)│      │  func <()    │
└──────┬────────┘      └──────┬────────┘      └──────┬────────┘
       │                      │                      │
       │                      │                      │
       ▼                      ▼                      ▼
  Compare values        Generate hash          Compare order
  property-by-property  from properties       using < operator
       │                      │                      │
       └──────────────┬───────┴──────────────┬───────┘
                      ▼                      ▼
               Used by collections       Used by sorting
               (Set, Dictionary)         algorithms
Myth Busters - 4 Common Misconceptions
Quick: Does conforming to Equatable automatically make your type Hashable? Commit to yes or no.
Common Belief:If a type is Equatable, it must also be Hashable automatically.
Tap to reveal reality
Reality:Equatable and Hashable are separate protocols; conforming to Equatable does not imply Hashable. You must declare and implement Hashable separately.
Why it matters:Assuming Equatable means Hashable can cause runtime errors when using your type in sets or dictionaries.
Quick: Can you define only the == operator to make your type Comparable? Commit to yes or no.
Common Belief:Defining == is enough to make a type Comparable.
Tap to reveal reality
Reality:Comparable requires defining the < operator; == is from Equatable. Defining == alone does not provide ordering.
Why it matters:Confusing equality with ordering leads to bugs when sorting or comparing values.
Quick: Does Swift always automatically generate Equatable and Hashable for any type? Commit to yes or no.
Common Belief:Swift automatically makes all types Equatable and Hashable without extra code.
Tap to reveal reality
Reality:Swift only synthesizes Equatable and Hashable for structs and enums whose properties themselves conform to these protocols.
Why it matters:Expecting automatic synthesis for unsupported types causes compilation errors and confusion.
Quick: Is it always best to use all properties for hashing and equality? Commit to yes or no.
Common Belief:Using all properties for hashing and equality is always correct and best.
Tap to reveal reality
Reality:Sometimes only a subset of properties should be used to define identity and hashing for correctness and performance.
Why it matters:Using irrelevant properties can cause incorrect behavior in collections or slow performance.
Expert Zone
1
Swift's automatic synthesis of Equatable and Hashable only works if all stored properties conform, but computed properties are ignored, which can cause subtle bugs if you expect them to participate.
2
Hashable's hash(into:) uses a Hasher struct that applies a cryptographic-quality hash function, making hash collisions rare but not impossible; understanding this helps in designing custom hash functions.
3
Comparable conformance can be combined with Equatable to provide total ordering, but care is needed to ensure consistency between == and < to avoid logical errors in sorting and searching.
When NOT to use
Avoid using automatic synthesis when your type's identity depends on only some properties or when you need custom comparison logic. Instead, manually implement Equatable, Hashable, or Comparable. For complex ordering, consider using custom comparator functions or the Comparable protocol with multiple criteria.
Production Patterns
In production, developers often conform model types to Equatable and Hashable to use them in sets and dictionaries efficiently. Comparable is used to sort lists, like sorting users by name or date. Custom implementations optimize performance by hashing only unique IDs or comparing only key fields, especially in large data sets or performance-critical apps.
Connections
Set Theory
Hashable enables types to be used in sets, which are collections of unique elements based on set theory.
Understanding how Hashable relates to set theory helps grasp why unique hash values are crucial for fast membership tests.
Database Indexing
Hashable's hashing concept is similar to database indexing, which uses keys to quickly find records.
Knowing database indexing principles clarifies why hashing improves lookup speed in collections.
Sorting Algorithms
Comparable provides the ordering needed by sorting algorithms to arrange data efficiently.
Understanding sorting algorithms deepens appreciation for how Comparable enables flexible and efficient data ordering.
Common Pitfalls
#1Assuming automatic Equatable synthesis works for classes with non-Equatable properties.
Wrong approach:class Person: Equatable { var name: String; var data: SomeNonEquatableType }
Correct approach:class Person: Equatable { var name: String; var data: SomeNonEquatableType; static func == (lhs: Person, rhs: Person) -> Bool { lhs.name == rhs.name } }
Root cause:Misunderstanding that automatic Equatable synthesis only works for structs/enums with Equatable properties.
#2Using all properties for hashing even when some do not affect identity.
Wrong approach:func hash(into hasher: inout Hasher) { hasher.combine(name); hasher.combine(age); hasher.combine(tempData) }
Correct approach:func hash(into hasher: inout Hasher) { hasher.combine(name); hasher.combine(age) }
Root cause:Not recognizing which properties define the unique identity of the type.
#3Defining Comparable without ensuring consistency with Equatable.
Wrong approach:static func < (lhs: Person, rhs: Person) -> Bool { lhs.age < rhs.age } // but == compares name only
Correct approach:static func == (lhs: Person, rhs: Person) -> Bool { lhs.age == rhs.age && lhs.name == rhs.name } static func < (lhs: Person, rhs: Person) -> Bool { lhs.age < rhs.age || (lhs.age == rhs.age && lhs.name < rhs.name) }
Root cause:Failing to keep equality and ordering logic aligned causes inconsistent behavior.
Key Takeaways
Equatable, Hashable, and Comparable protocols let Swift compare, hash, and order your custom types automatically or with minimal code.
Swift can generate Equatable and Hashable implementations for structs and enums if all their properties conform, saving you from writing repetitive code.
Comparable requires defining the < operator, and Swift provides other comparison operators based on it, enabling easy sorting.
Customizing hashing and equality logic is important for performance and correctness when your type's identity depends on specific properties.
Understanding these protocols deeply helps you write efficient, clean, and bug-free Swift code that works well with collections and sorting.