Por que existem dois sinais de igualdade em Swift e o que eles escondem?
\n\nVocê já escreveu em Swift e de repente se deparou com o estranho operador ===? O primeiro pensamento: "Erro de digitação? Ou é algum tipo de super-igual?" Na verdade, não é um erro de digitação, mas um mecanismo poderoso que muda radicalmente a lógica de comparação. Se você trabalha com classes, estruturas ou closures, entender a diferença entre == e === não é teoria, é uma necessidade. Sem isso, você corre o risco de ter bugs difíceis de detectar.
Neste artigo, vamos olhar "por baixo dos panos" do Swift, analisar como ambos os operadores funcionam e aprender a escolher a ferramenta certa para cada tarefa. Pronto? Vamos lá.
\n\nO que é o operador de igualdade ==?
\n\nO operador == é o seu "amigo" padrão para comparar valores. Ele verifica se dois objetos são iguais em termos de conteúdo. Mas há um detalhe: o Swift não sabe o que significa "iguais" para o seu próprio tipo de dado. Portanto, para usar ==, seu tipo deve estar em conformidade com o protocolo Equatable.
Veja como funciona para tipos embutidos:
\n\nlet a = 5\nlet b = 5\nprint(a == b) // true, porque os valores são iguais\n\nlet name1 = "Alice"\nlet name2 = "Alice"\nprint(name1 == name2) // true, as strings são iguais\n\nPara classes e estruturas, você precisa implementar Equatable manualmente. Por exemplo:
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, o conteúdo é igual\n\nObserve: == compara valores, não referências. Para estruturas isso é óbvio, mas com classes começa a parte interessante.
Operador de identidade ===: quando dois objetos são a mesma coisa
\n\nO operador === (três sinais de igual) verifica não o conteúdo, mas a identidade das referências. Ele responde à pergunta: "As duas variáveis apontam para o mesmo objeto na memória?"
Este operador funciona apenas com tipos por referência: classes e closures (closures também são tipos por referência). Para estruturas e enumerações, ele simplesmente não existe — o compilador emitirá um erro.
\n\nVeja o exemplo:
\n\nclass 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, se Equatable não foi implementado\nprint(car1 === car2) // false, objetos diferentes\nprint(car1 === car3) // true, mesma referência\n\nAqui, car1 e car2 são dois objetos diferentes com o mesmo conteúdo. Já car3 é apenas outro nome para o mesmo objeto que car1. === mostra isso claramente.
Como funciona por baixo dos panos: memória e ponteiros
\n\nPara entender a diferença, precisamos olhar para a memória. Quando você cria uma instância de uma classe, o Swift aloca um espaço para ela no heap e retorna um ponteiro para esse espaço. A variável armazena exatamente o ponteiro, não o objeto em si.
\n\n- \n
==compara o conteúdo através do ponteiro: ele chama sua implementação deEquatable, que pode examinar os campos do objeto. \n ===compara os próprios ponteiros: se dois ponteiros apontam para o mesmo endereço de memória, o resultado étrue. \n
Para estruturas, é mais simples: elas são armazenadas na stack e copiadas quando atribuídas. Portanto, === não faz sentido para elas — duas estruturas diferentes serão sempre objetos diferentes na memória, mesmo que seu conteúdo seja