Generic Types / where Clause
| Since: | Swift 1.0(2014) |
|---|
A generic type in Swift is a struct, class, or enum that has one or more type parameters. The following covers advanced usage — make sure you understand the basics of generics before reading on.
Using a where clause, you can express complex constraints on those type parameters.
Syntax
// Generic struct
struct TypeName<T> {
var items: [T]
mutating func push(_ item: T) { items.append(item) }
mutating func pop() -> T? { return items.popLast() }
}
// Generic function with a where clause
func functionName<T, U>(_ t: T, _ u: U) where T: Equatable, U: Hashable {
// body
}
// Extension with a where clause (conditional extension)
extension Array where Element: Numeric {
var sum: Element { return reduce(0, +) }
}
Syntax reference
| Syntax | Description |
|---|---|
| struct Name<T> { } | Defines a generic structure with a type parameter. |
| where T: Protocol | Constrains a type parameter to conform to a protocol. |
| where T == U | Requires that two type parameters are the same type. |
| associatedtype | Defines a type placeholder used inside a protocol. |
| extension Type<T> where T: Constraint | Defines an extension that is only available when the given condition is met. |
Sample code
sample_generic_type_where.swift
// A generic stack
struct Stack<T> {
private var items: [T] = []
mutating func push(_ item: T) {
items.append(item)
}
mutating func pop() -> T? {
return items.popLast()
}
var top: T? { return items.last }
var isEmpty: Bool { return items.isEmpty }
var count: Int { return items.count }
}
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
intStack.push(3)
print("Stack top: \(intStack.top!)")
print("pop: \(intStack.pop()!)")
// A protocol with an associated type
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
// Constraining the associated type with a where clause
func compareContainers<C1, C2>(_ c1: C1, _ c2: C2) -> Bool
where C1: Container, C2: Container, C1.Item == C2.Item, C1.Item: Equatable {
guard c1.count == c2.count else { return false }
for i in 0..<c1.count {
if c1[i] != c2[i] { return false }
}
return true
}
// Conditional extension
extension Stack: Equatable where T: Equatable {
static func == (lhs: Stack, rhs: Stack) -> Bool {
return lhs.items == rhs.items
}
}
let stack1 = Stack<Int>()
let stack2 = Stack<Int>()
print("Empty stack comparison: \(stack1 == stack2)")
Running the above produces the following output:
swift generic_type_where.swift Stack top: 3 pop: 3 Empty stack comparison: true
Overview
Generic types let you define reusable data structures that work with different types. Swift's standard library types — Array, Dictionary, and Optional — are all implemented as generic types.
Unlike the simple : constraint syntax, a where clause can express multiple constraints and relationships between types (such as T.Element == U). Protocols with associated types cannot be used as regular types; they can only appear inside generic constraints (where clauses). Starting with Swift 5.7, you can use them as existentials with the any ProtocolName syntax.
For details on some and any, see some / any / opaque type.
If you find any errors or copyright issues, please contact us.