When defining a protocol, it’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that’s used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype
keyword.
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
Any type that conforms to the Container
protocol must be able to specify the type of values it stores. Thanks to Swift’s type inference, you don’t actually need to declare a concrete Item
as part of the definition.
struct Stack<Element>: Container {
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
Protocols can declare one or more primary associated types using a syntax similar to a generic parameter list of a concrete type:
protocol Sequence<Element> {
associatedtype Element
associatedtype Iterator : IteratorProtocol where Element == Iterator.Element
}
protocol DictionaryProtocol<Key, Value> {
associatedtype Key : Hashable
associatedtype Value
}
A protocol with primary associated types can be written with a list of type arguments in angle brackets, from any position where a protocol conformance requirement was previously allowed.
func readSyntaxHighlightedLines(_ file: String) -> some Sequence<[Token]> {}
func concatenate<S : Sequence<String>>(_ lhs: S, _ rhs: S) -> S {}
Primary associated types add a new facet to the design of protocols. For every public protocol with associated type requirements, we need to carefully consider which of them we want to mark as primary. Once a protocol gains a primary associated type annotation, most subsequent changes would be source-breaking.