== and === in Swift: What's the Difference and What's Under the Hood

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why does Swift have two equality signs and what do they hide?

\n\n

Have you ever written code in Swift and suddenly stumbled upon the strange === operator? Your first thought: "A typo? Or is this some kind of super-equals?" In fact, it's not a typo, but a powerful mechanism that fundamentally changes the logic of comparison. If you work with classes, structs, or closures, understanding the difference between == and === is not theory, but a necessity. Without it, you risk introducing bugs that are hard to catch.

\n\n

In this article, we'll look under the hood of Swift, break down how both operators work, and learn to choose the right tool for each task. Ready? Let's go.

\n\n

What is the equality operator ==?

\n\n

The == operator is your standard "friend" for comparing values. It checks whether two objects are equal based on their content. But there's a catch: Swift doesn't know what "equal" means for your custom data type. Therefore, to use ==, your type must conform to the Equatable protocol.

\n\n

Here's how it looks for built-in types:

\n\n
let a = 5\nlet b = 5\nprint(a == b) // true, because the values are the same\n\nlet name1 = "Alice"\nlet name2 = "Alice"\nprint(name1 == name2) // true, the strings are the same
\n\n

For classes and structs, you need to implement Equatable yourself. For example:

\n\n
struct User {\n    let id: Int\n    let name: String\n}\n\nextension User: Equatable {\n    static func == (lhs: User, rhs: User) -> Bool {\n        return lhs.id == rhs.id && lhs.name == rhs.name\n    }\n}\n\nlet user1 = User(id: 1, name: "John")\nlet user2 = User(id: 1, name: "John")\nprint(user1 == user2) // true, the content matches
\n\n

Note: == compares values, not references. For structs this is obvious, but with classes, things get interesting.

\n\n

The identity operator ===: when two objects are the same thing

\n\n

The === operator (triple equals) checks not the content, but the identity of references. It answers the question: "Do two variables point to the same object in memory?"

\n\n

This operator works only with reference types: classes and closures (closures are also reference types). For structs and enums, it simply doesn't exist — the compiler will give an error.

\n\n

Look at this example:

\n\n
class Car {\n    var model: String\n    init(model: String) {\n        self.model = model\n    }\n}\n\nlet car1 = Car(model: "Tesla")\nlet car2 = Car(model: "Tesla")\nlet car3 = car1\n\nprint(car1 == car2) // false, if Equatable is not implemented\nprint(car1 === car2) // false, different objects\nprint(car1 === car3) // true, the same reference
\n\n

Here, car1 and car2 are two different objects with the same content. And car3 is just another name for the same object as car1. === clearly shows this.

\n\n

How it works under the hood: memory and pointers

\n\n

To understand the difference, you need to look into memory. When you create an instance of a class, Swift allocates a region for it on the heap and returns a pointer to that region. The variable stores the pointer, not the object itself.

\n\n
    \n
  • == compares the content via the pointer: it calls your Equatable implementation, which can look at the object's fields.
  • \n
  • === compares the pointers themselves: if two pointers point to the same memory address, the result is true.
  • \n
\n\n

For structs, it's simpler: they are stored on the stack and are copied upon assignment. Therefore, === makes no sense for them — two different structs will always be different objects in memory, even if their content is

Blogs

Book Recommendations