Protocols may define associated type requirements using the associatedtype
keyword:
protocol Container {
associatedtype Element
var count: Int { get }
subscript(index: Int) -> Element { get set }
}
Protocols with associated type requirements can only be used as generic constraints:
// These are NOT allowed, because Container has associated type requirements:
func displayValues(container: Container) { ... }
class MyClass { let container: Container }
// > error: protocol 'Container' can only be used as a generic constraint
// > because it has Self or associated type requirements
// These are allowed:
func displayValues<T: Container>(container: T) { ... }
class MyClass<T: Container> { let container: T }
A type which conforms to the protocol may satisfy an associatedtype
requirement implicitly, by providing a given type where the protocol expects the associatedtype
to appear:
struct ContainerOfOne<T>: Container {
let count = 1 // satisfy the count requirement
var value: T
// satisfy the subscript associatedtype requirement implicitly,
// by defining the subscript assignment/return type as T
// therefore Swift will infer that T == Element
subscript(index: Int) -> T {
get {
precondition(index == 0)
return value
}
set {
precondition(index == 0)
value = newValue
}
}
}
let container = ContainerOfOne(value: "Hello")
(Note that to add clarity to this example, the generic placeholder type is named T
– a more suitable name would be Element
, which would shadow the protocol's associatedtype Element
. The compiler will still infer that the generic placeholder Element
is used to satisfy the associatedtype Element
requirement.)
An associatedtype
may also be satisfied explicitly through the use of a typealias
:
struct ContainerOfOne<T>: Container {
typealias Element = T
subscript(index: Int) -> Element { ... }
// ...
}
The same goes for extensions:
// Expose an 8-bit integer as a collection of boolean values (one for each bit).
extension UInt8: Container {
// as noted above, this typealias can be inferred
typealias Element = Bool
var count: Int { return 8 }
subscript(index: Int) -> Bool {
get {
precondition(0 <= index && index < 8)
return self & 1 << UInt8(index) != 0
}
set {
precondition(0 <= index && index < 8)
if newValue {
self |= 1 << UInt8(index)
} else {
self &= ~(1 << UInt8(index))
}
}
}
}
If the conforming type already satisfies the requirement, no implementation is needed:
extension Array: Container {} // Array satisfies all requirements, including Element