Что такое протокол с обобщенными типами (Generics) в Swift?

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

Ответ

Протокол с обобщенными типами — это протокол, который использует ассоциированные типы (associatedtype), позволяя определять абстрактные типы, которые будут конкретизированы при его реализации или использовании. Это аналог дженериков для протоколов.

Зачем это нужно? Без ассоциированных типов протокол был бы вынужден работать с конкретными типами (например, Int, String), что сильно ограничило бы его переиспользуемость.

Базовый пример:

// Протокол с ассоциированным типом Item
protocol Container {
    associatedtype Item // Абстрактный тип "элемент контейнера"
    var count: Int { get }
    mutating func append(_ item: Item)
    subscript(i: Int) -> Item { get }
}

// Реализация для стека. Element становится конкретным типом для Item.
struct Stack<Element>: Container {
    // Явное указание связи (можно опустить, компилятор выведет сам)
    typealias Item = Element

    private var items = [Element]()
    var count: Int { items.count }

    mutating func append(_ item: Element) {
        items.append(item)
    }

    subscript(i: Int) -> Element {
        return items[i]
    }
}

// Использование
var intStack = Stack<Int>()
intStack.append(1)
print(intStack[0]) // 1

Ограничения с помощью where (Generic Where Clauses): Можно наложить ограничения на ассоциированный тип, например, требовать поддержки сравнения.

protocol ComparableContainer {
    associatedtype Item: Equatable // Item должен соответствовать Equatable
    func contains(_ item: Item) -> Bool
}

extension Array: ComparableContainer where Element: Equatable {
    typealias Item = Element
    func contains(_ item: Element) -> Bool { self.contains(item) }
}

Ключевые моменты:

  • associatedtype создает placeholder для типа в протоколе.
  • Конкретный тип определяется в типах, реализующих протокол (структурах, классах, перечислениях).
  • Протокол с associatedtype нельзя использовать как самостоятельный тип (например, var container: Container). Вместо этого используют дженерик-ограничения (some Container, any Container в Swift 5.7+) или opaque types (some Container).
  • Это основа для создания гибких и типобезопасных абстракций, таких как Collection и Sequence в стандартной библиотеке Swift.