What are protocol associated types in Swift

Mar 06, 2023#swift#protocols

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]
    }
}

Primary Associated Types

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.

Learn more: SE-0346, SE-0358