Зачем протокол Hashable требует реализации оператора равенства (==)?

«Зачем протокол Hashable требует реализации оператора равенства (==)?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Протокол Hashable наследует протокол Equatable. Поэтому реализация == является обязательным требованием.

Логическая причина: Для корректной работы хеш-таблиц (как в Set или Dictionary) должно выполняться ключевое правило:

Если два объекта равны (a == b возвращает true), то их хеш-значения должны быть одинаковыми (a.hashValue == b.hashValue).

Обратное утверждение не требуется — разные объекты могут иметь одинаковый хеш (коллизия), и структура данных корректно это обработает.

Пример реализации:

struct User: Hashable {
    let id: UUID
    let username: String

    // 1. Определяем равенство по уникальному id
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id
    }

    // 2. В хеш включаем только id, соблюдая правило
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

// Использование в Set
let user1 = User(id: uuid1, username: "Alice")
let user2 = User(id: uuid1, username: "Alice_Changed") // Тот же id!
print(user1 == user2) // true, т.к. id совпадают
print(Set([user1, user2]).count) // 1. В Set попал только один объект

Последствия нарушения правила: Если равные объекты дают разный хеш, они могут быть неправильно размещены в Set или Dictionary, что приведет к логическим ошибкам (дубликатам или потере данных).