Что необходимо, чтобы использовать структуру (struct) в качестве ключа словаря в Swift?

Ответ

Чтобы тип мог быть ключом в Dictionary, он должен соответствовать протоколу Hashable. Для структуры это означает:

  1. Реализовать Hashable: Протокол требует реализации метода hash(into:), который объединяет хэши всех значимых для равенства свойств.
  2. Реализовать Equatable: Поскольку Hashable наследуется от Equatable, необходимо определить оператор == для сравнения двух экземпляров.

Пример ручной реализации:

struct Coordinate: Hashable {
    let latitude: Double
    let longitude: Double

    // 1. Реализация hash(into:)
    func hash(into hasher: inout Hasher) {
        hasher.combine(latitude)
        hasher.combine(longitude)
    }

    // 2. Реализация Equatable
    static func == (lhs: Coordinate, rhs: Coordinate) -> Bool {
        return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
    }
}

// Использование в качестве ключа
var cache: [Coordinate: String] = [:
cache[Coordinate(latitude: 55.75, longitude: 37.61)] = "Москва"

Важно: Swift поддерживает автоматический синтез соответствия протоколам Hashable и Equatable, если все свойства структуры также им соответствуют. В этом случае явно реализовывать методы не нужно.

Ответ 18+ 🔞

Э, слушай, смотри, вот тебе на пальцах, как этот ваш Dictionary работает с ключами. Чтобы твою структуру можно было запихнуть в ключи, ей нужно быть, как это говорят, Hashable. А это, блядь, значит две вещи, запоминай:

  1. Хэшировать себя уметь: Надо реализовать метод hash(into:). Туда ты засовываешь все свои значимые свойства, чтобы хэш был уникальный, как отпечаток пальца, ну почти.
  2. Сравнивать себя уметь: Ага, а ещё Hashable — это сын Equatable. Так что оператор == для сравнения двух твоих структур тоже нужен. Иначе как словарь поймёт, что ключ-то тот же самый?

Вот смотри, как это вручную делается, если ты такой умный и хочешь всё контролировать:

struct Coordinate: Hashable {
    let latitude: Double
    let longitude: Double

    // 1. Вот этот метод, где мы всё комбинируем
    func hash(into hasher: inout Hasher) {
        hasher.combine(latitude)
        hasher.combine(longitude)
    }

    // 2. А вот сравнение, чтоб было понятно, что две точки — это одна и та же хуйня
    static func == (lhs: Coordinate, rhs: Coordinate) -> Bool {
        return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
    }
}

// И теперь можно юзать, ебать его в сраку
var cache: [Coordinate: String] = [:]
cache[Coordinate(latitude: 55.75, longitude: 37.61)] = "Москва"

Но есть же и хорошие новости, ёпта! Swift — он не совсем тупой. Если все свойства твоей структуры сами по себе HashableDouble, String, Int — они такие), то можно просто написать : Hashable и нихуя не делать! Компилятор сам всё сгенерит, как шарманка. Это называется автоматический синтез, и он — наше всё. Главное, не выёбывайся зря.