Как вы используете Generics (обобщения) в Swift?

Ответ

Generics позволяют писать типобезопасный и переиспользуемый код, избегая дублирования.

1. Функции с Generics:

func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

var x = 5, y = 10
swapValues(&x, &y) // x = 10, y = 5

2. Кастомные типы с Generics:

struct Stack<Element> {
    private var elements = [Element]()

    mutating func push(_ element: Element) {
        elements.append(element)
    }

    mutating func pop() -> Element? {
        return elements.popLast()
    }
}

var intStack = Stack<Int>()
intStack.push(42)

3. Ограничения типов (Type Constraints):

func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
    return array.firstIndex { $0 == value }
}

4. Протоколы с Associated Types:

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
}

Преимущества:

  • Типобезопасность — компилятор проверяет типы
  • Производительность — нет накладных расходов как с Any
  • Переиспользование кода — одна реализация для разных типов

Использование в стандартной библиотеке:

  • Array<Element>
  • Dictionary<Key, Value>
  • Optional<Wrapped>

Ответ 18+ 🔞

А, слушай, смотри, вот эта вся хуйня с дженериками — это вообще огонь, если понять, нахуя она нужна. Без них пиздец как неудобно, честно.

Ну, типа, представь: пишешь ты функцию, чтобы два значения поменять местами. Без дженериков пришлось бы для каждого типа свою функцию городить, блядь. А так — раз, и на все случаи жизни:

func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

var x = 5, y = 10
swapValues(&x, &y) // x = 10, y = 5

Видишь этот <T>? Это типа "эй, компилятор, я тут с каким-то типом работаю, назову его T, а ты сам потом догадаешься, с каким именно, когда я буду вызывать эту функцию". И он реально догадывается, ёпта! Подставит Int, если числа, String, если строки. Умная жопа.

А вот ещё прикол — можно свои типы с дженериками делать. Смотри, стек, например:

struct Stack<Element> {
    private var elements = [Element]()

    mutating func push(_ element: Element) {
        elements.append(element)
    }

    mutating func pop() -> Element? {
        return elements.popLast()
    }
}

var intStack = Stack<Int>()
intStack.push(42)

Создал Stack<Int> — и всё, внутри только целые числа. Создал Stack<String> — и там уже строки болтаются. Одна структура, а работает с чем угодно. Красота, блядь.

Но иногда надо, чтобы этот твой абстрактный тип T не совсем уж левым был. Ну, например, чтобы его можно было сравнивать. И тут на сцену выходят ограничения, type constraints:

func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
    return array.firstIndex { $0 == value }
}

Вот смотри: <T: Equatable>. Это как приказ: "ладно, T — любой тип, но, сука, он должен уметь сравниваться через ==". Иначе компилятор тебе в ебальник такой ошибкой швырнёт, что мало не покажется. Зато внутри функции можно спокойно $0 == value писать, и всё будет тип-топ.

А ещё есть в протоколах такая штука — associatedtype. Это, блядь, как дженерик, но для протоколов. Объявляешь какой-то абстрактный внутренний тип, а уже при реализации каждый сам решает, чем его заполнить.

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
}

Сказал — и пошёл реализовывать. В одной структуре Item будет Int, в другой — String. Гибко, ёпта.

И в чём, собственно, профит, спросишь ты? А профит, блядь, налицо:

  • Типобезопасность. Компилятор следит, чтобы ты не сунул кота в массив собак. Никаких сюрпризов в рантайме.
  • Производительность. Всё проверяется на этапе компиляции, никаких накладных расходов, как если бы ты через Any всё писал. Быстро, чётко.
  • Переиспользование кода. Написал алгоритм один раз — и он работает с любым типом. Не надо копипастить одно и то же, меняя только Int на String.

Да ты посмотри вокруг — вся стандартная библиотека на этом стоит! Array<Element>, Dictionary<Key, Value>, даже этот ёбанный Optional<Wrapped> — всё дженерики, блядь. Так что не игнорируй эту тему, а то так и будешь копипастить функции, как последний распиздяй.