Swift Where Clauses
A where clause adds requirements to a generic declaration. The requirements can link placeholder types together or limit the element type of a collection.
Two collections with the same element type
The shareElement function takes two collections with different placeholders. Its where clause requires both collections to share one element type, and that element type must conform to Equatable.
func shareElement<A: Collection, B: Collection>(_ first: A, _ second: B) -> Bool where A.Element == B.Element, A.Element: Equatable {
for item in first {
for other in second {
if item == other {
return true
}
}
}
return false
}
print(shareElement([1, 2, 3], [5, 3]))
print(shareElement(["a", "b"], ["c"]))An extension only for arrays of Int
The extension applies to Array only when the element type is Int. The total method can therefore add the elements with the + operator.
extension Array where Element == Int {
func total() -> Int {
var sum = 0
for value in self {
sum += value
}
return sum
}
}
print([1, 2, 3, 4].total())
print([10, 20, 5].total())An extension only for arrays of String
The extension applies to Array only when the element type is String. The longest method compares the count of each word and keeps the longest one.
extension Array where Element == String {
func longest() -> String {
var best = ""
for word in self {
if word.count > best.count {
best = word
}
}
return best
}
}
print(["cat", "elephant", "dog"].longest())
print(["sun", "moon"].longest())An extension for Equatable elements
The extension applies to Array when the element type conforms to Equatable. The countOccurrences method can therefore compare each element with the target using ==.
extension Array where Element: Equatable {
func countOccurrences(of target: Element) -> Int {
var count = 0
for item in self {
if item == target {
count += 1
}
}
return count
}
}
print([1, 2, 1, 3, 1].countOccurrences(of: 1))
print(["a", "b", "a"].countOccurrences(of: "a"))Example
func shareElement<A: Collection, B: Collection>(_ first: A, _ second: B) -> Bool where A.Element == B.Element, A.Element: Equatable {
for item in first {
for other in second {
if item == other {
return true
}
}
}
return false
}
extension Array where Element == Int {
func total() -> Int {
var sum = 0
for value in self {
sum += value
}
return sum
}
}
print([4, 5, 6].total())
print(shareElement([4, 5, 6], [9, 5]))Key points
- A where clause appears after the declaration and starts with the where keyword.
- A requirement can state that two placeholders share the same element type.
- A requirement can also state that an element type conforms to a protocol.
- An extension can use a where clause to add methods only to collections of one element type.
- An extension can use a where clause to add methods only when the element type conforms to a protocol.
In this example, one program holds a generic function with a same-element requirement and an extension limited to arrays of Int. The total print shows 15 for the numbers 4, 5, and 6. The share print shows true because both arrays hold the number 5.
Note
The compiler checks every requirement before a generic function or an extension method can be used.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does the requirement Element == Int in an Array extension state?