## PATs - protocol with ssociated types (Связанные типы)

1. - [Что такое протоколы с Associated Types?](https://medium.com/@tesnik/что-такое-протоколы-с-associated-types-8065c87b6a6e)

При определении протокола полезно объявить один или несколько ассоциированных типов как часть определения протокола. 

Ассоциированный тип дает условное имя типу, который используется как часть протокола. Фактический тип, который будет использоваться для ассоциированного типа, не указывается до тех пор, пока протокол не будет принят. Ассоциированные типы указываются с помощью ключевого слова associatedtype.

### Пример протокола Container, объявляющий ассоциированный тип Item:

```swift
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
```

Протокол Container определяет три необходимые возможности, которые должен предоставлять любой контейнер:

* Должна быть возможность добавить новый элемент в контейнер с помощью метода append(_:).

* Должна быть возможность доступа к подсчету элементов в контейнере через свойство count, которое возвращает значение Int.

* Должна быть возможность извлечения каждого элемента в контейнере с помощью подскрипта, который принимает значение индекса Int.

Этот протокол не определяет, как должны храниться элементы в контейнере или какого типа они могут быть. Протокол определяет только три бита функциональности, которые должен обеспечить любой тип, чтобы считаться контейнером. Соответствующий тип может предоставлять дополнительную функциональность, если он удовлетворяет этим трем требованиям.

Любой тип, соответствующий протоколу Container, должен иметь возможность определять тип значений, которые он хранит. В частности, он должен гарантировать, что в контейнер добавляются только элементы нужного типа, и должен четко определять тип элементов, возвращаемых его подскриптом.

Чтобы определить эти требования, протоколу Container необходим способ ссылаться на тип элементов, которые будет хранить контейнер, не зная, каков этот тип для конкретного контейнера. Протокол Container должен определить, что любое значение, передаваемое методу append(_:), должно иметь тот же тип, что и тип элемента контейнера, и что значение, возвращаемое подскриптом контейнера, будет иметь тот же тип, что и тип элемента контейнера.

Для этого в протоколе Container объявляется ассоциированный тип Item, записываемый как associatedtype Item. Протокол не определяет, что такое Item - эту информацию может предоставить любой соответствующий тип. Тем не менее, псевдоним Item предоставляет возможность ссылаться на тип элементов в контейнере и определять тип для использования с методом append(_:) и subscript, чтобы обеспечить ожидаемое поведение любого контейнера.

Вот версия негенерического типа IntStack из Generic Types выше, адаптированная для соответствия протоколу Container:

```swift
struct IntStack: Container {
    // original IntStack implementation
    var items: [Int] = []
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
    // conformance to the Container protocol
    typealias Item = Int
    mutating func append(_ item: Int) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Int {
        return items[i]
    }
}
```

Тип IntStack реализует все три требования протокола Container и в каждом случае оборачивает часть существующей функциональности типа IntStack для удовлетворения этих требований.

Более того, IntStack определяет, что для данной реализации Container подходящим Item является тип Int. Определение typealias Item = Int превращает абстрактный тип Item в конкретный тип Int для данной реализации протокола Container.

Благодаря выводу типов в Swift, вам не нужно объявлять конкретный тип Item = Int как часть определения IntStack. Поскольку IntStack соответствует всем требованиям протокола Container, Swift может определить подходящий Item, просто посмотрев на тип параметра item метода append(_:) и возвращаемый тип подскрипта. Действительно, если удалить строку typealias Item = Int из приведенного выше кода, все будет работать, поскольку ясно, какой тип следует использовать для Item.

Вы также можете сделать общий тип Stack соответствующим протоколу Container:

```swift
struct Stack<Element>: Container {
    // original Stack<Element> implementation
    var items: [Element] = []
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
    // conformance to the Container protocol
    mutating func append(_ item: Element) {
        self.push(item)
    }
    var count: Int {
        return items.count
    }
    subscript(i: Int) -> Element {
        return items[i]
    }
}
```

На этот раз параметр типа Element используется в качестве типа параметра элемента метода append(_:) и возвращаемого типа субскрипта. Поэтому Swift может сделать вывод, что Element является подходящим типом для использования в качестве Item для данного конкретного контейнера.

---

[5.4.1 Protocol Theme](./5.4.1%20Protocol.md) | [Back To iTWiki Contents](https://github.com/eldaroid/iTWiki) | [5.5 Abstract Mechanism Theme Folder](../5.5%20AbstractMechanism/)