Что такое протокол Hashable в Swift и как он работает?

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

Ответ

Hashable — это протокол, который позволяет типу быть использованным в качестве ключа в Dictionary или элемента в Set. Он является наследником протокола Equatable.

Для чего нужен: Для эффективного хранения и поиска объектов в хеш-таблицах (лежащих в основе Set и Dictionary).

Основное требование: Должна быть реализована функция hash(into:), которая комбинирует хеш-значения всех значимых свойств типа.

Пример реализации для кастомной структуры:

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

    // Реализация Hashable (и Equatable) может быть синтезирована компилятором,
    // если все свойства соответствуют Hashable.
    // Ручная реализация требуется для выбора конкретных свойств.
    func hash(into hasher: inout Hasher) {
        // В хеш включаем только id и username, email игнорируем.
        hasher.combine(id)
        hasher.combine(username)
    }

    // Соответствующая реализация Equatable (обязательна для Hashable).
    static func == (lhs: User, rhs: User) -> Bool {
        return lhs.id == rhs.id && lhs.username == rhs.username
    }
}

// Использование в Set и Dictionary
let userSet: Set<User> = [User(id: uuid1, username: "alex", email: "a@test.com")]
let userScores: [User: Int] = [user1: 100]

Важнейшее правило (контракт Hashable):

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

Обратное НЕ верно: Разные объекты (a != b) могут иметь одинаковые хеш-значения (это коллизия). Качественная hash(into:) функция минимизирует вероятность коллизий.

Как работает Hasher: Swift использует детерминированный, но не криптографический хеш-алгоритм. Значение хеша может меняться между запусками программы (по умолчанию), что является мерой безопасности. Для тестов это поведение можно отключить через переменную окружения.

Автоматический синтез (Swift 4.1+): Для структур и перечислений, все свойства которых соответствуют Hashable, компилятор автоматически генерирует реализации hash(into:) и ==.