Swift Type Constraints
A type constraint is a limit placed on a generic placeholder. It names a protocol that the filling type must conform to.
Searching with an Equatable constraint
The firstIndex function is constrained to Equatable, so it can compare each element with ==. A manual counter tracks the position while the loop walks the array, and a missing value gives back -1.
func firstIndex<T: Equatable>(of target: T, in items: [T]) -> Int {
var index = 0
for item in items {
if item == target {
return index
}
index += 1
}
return -1
}
print(firstIndex(of: 30, in: [10, 20, 30, 40]))
print(firstIndex(of: "cy", in: ["ana", "bo", "cy"]))
print(firstIndex(of: 99, in: [10, 20]))The largest with a Comparable constraint
The largest function is constrained to Comparable, so it can order its three values with >. It returns the greatest of the numbers and then the greatest of the words.
func largest<T: Comparable>(_ first: T, _ second: T, _ third: T) -> T {
var best = first
if second > best {
best = second
}
if third > best {
best = third
}
return best
}
print(largest(3, 9, 5))
print(largest("apple", "pear", "fig"))A call that breaks the constraint
The Plain struct does not conform to Equatable, so it cannot fill the placeholder of firstIndex. The compiler rejects the call, and the stated output is the first error line from that compile.
struct Plain {
var value: Int
}
func firstIndex<T: Equatable>(of target: T, in items: [T]) -> Int {
var index = 0
for item in items {
if item == target {
return index
}
index += 1
}
return -1
}
let items = [Plain(value: 1), Plain(value: 2)]
print(firstIndex(of: Plain(value: 1), in: items))Example
func firstIndex<T: Equatable>(of target: T, in items: [T]) -> Int {
var index = 0
for item in items {
if item == target {
return index
}
index += 1
}
return -1
}
func largest<T: Comparable>(_ first: T, _ second: T, _ third: T) -> T {
var best = first
if second > best {
best = second
}
if third > best {
best = third
}
return best
}
print(firstIndex(of: "bo", in: ["ana", "bo", "cy"]))
print(largest(12, 7, 20))Key points
- A constraint is written after the placeholder name, with a colon and the protocol name.
- An Equatable constraint lets a function compare values with == inside its body.
- A Comparable constraint lets a function order values with < and > inside its body.
- A type that does not conform to the named protocol cannot fill the placeholder.
- When a call breaks a constraint, the compiler reports an error and the program does not run.
In this example, one program holds a search function constrained to Equatable and a largest function constrained to Comparable. The search finds bo at index 1 in its word array. The largest call compares 12, 7, and 20 and prints 20.
Note
A constraint narrows the types a generic function accepts, and in return it guarantees the operations the function needs.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why does the compiler reject a call that passes Plain values to a function constrained to Equatable?