Для чего используется протокол Comparable в Swift?

Ответ

Протокол Comparable наследуется от Equatable и позволяет определять порядок для экземпляров пользовательского типа с помощью операторов сравнения (<, <=, >, >=).

Основные сценарии использования:

  • Сортировка коллекций. Методы sorted(), sort() для массивов и других коллекций требуют, чтобы элементы были Comparable.
    let numbers = [5, 2, 8, 1]
    let sortedNumbers = numbers.sorted() // [1, 2, 5, 8]
  • Работа с алгоритмами и структурами данных, которые зависят от упорядочивания (например, бинарный поиск, нахождение минимума/максимума).
  • Использование в условиях if, guard, switch.
    if currentScore > highScore { highScore = currentScore }
  • Создание типов с естественным порядком (даты, версии, приоритеты).

Пример реализации для пользовательского типа:

struct Person: Comparable {
    let name: String
    let age: Int

    // Требуется реализовать только оператор `<`.
    // Остальные операторы (`<=`, `>`, `>=`) генерируются компилятором автоматически.
    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.age < rhs.age
    }

    // Также требуется Equatable (можно использовать синтезированную реализацией).
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}

let people = [Person(name: "Alice", age: 30), Person(name: "Bob", age: 25)]
let sortedByAge = people.sorted() // [Bob(25), Alice(30)]

Ответ 18+ 🔞

А, Comparable, ёпта! Ну это ж тот самый протокол, который из Equatable вылез, как глист из жопы, и позволяет тебе твои кастомные типы сравнивать операторами вроде < или >. Не просто "равно/не равно", а вот это вот всё — кто больше, кто меньше, кто впереди планеты всей.

Ну и зачем это вообще нужно, спросишь ты?

  • Сортировка, мать её. Коллекции твои — массивы там всякие — хотят sorted() или sort() вызвать? А элементы-то Comparable должны быть, иначе получишь пиздюлину от компилятора. Без этого — никуда.
    let numbers = [5, 2, 8, 1]
    let sortedNumbers = numbers.sorted() // [1, 2, 5, 8] — вот так, по-порядку, как в армии.
  • Алгоритмы разные, структуры данных. Бинарный поиск, минимум/максимум найти — всё это ебётся об порядок. Без Comparable они просто пальцы веером.
  • В условиях использовать. Ну, if score > highScore — классика жанра, что тут объяснять.
  • Типы с естественным порядком сделать. Дата, версия программы, приоритет задачи — тут само собой напрашивается, что одно больше другого.

А вот смотри, как свою структуру научить сравниваться, на примере Person:

struct Person: Comparable {
    let name: String
    let age: Int

    // Внимание, хуле! Реализовать нужно ТОЛЬКО оператор `<`.
    // Всё остальное — `<=`, `>`, `>=` — компилятор сам сгенерит, умная жопа.
    static func < (lhs: Person, rhs: Person) -> Bool {
        return lhs.age < rhs.age // Сравниваем по возрасту. Молодой — значит "меньше".
    }

    // Ну и Equatable тоже нужен (он же родитель). Можно синтезированную реализацию использовать.
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.age == rhs.age && lhs.name == rhs.name
    }
}

let people = [Person(name: "Alice", age: 30), Person(name: "Bob", age: 25)]
let sortedByAge = people.sorted() // [Bob(25), Alice(30)] — Боб моложе, вот он и впереди.

Вот и вся магия. Объявил протокол, один оператор написал — и пошло-поехало, сортируй, ищи, сравнивай. Главное — логику в < правильно засунуть, а то получится, что старик будет "меньше" юнца, и все твои алгоритмы накроются медным тазом.