Lesson 91Advanced Topics
Generic Functions
What are Generics?
Generics let you write flexible, reusable functions and types that work with any type. Instead of writing separate functions for Int, String, etc., write one generic function that works with all types!
Syntax
func functionName<T>(param: T) -> T
T is a type placeholder - Swift infers the actual type from usage.
Type Constraints
Protocol Constraint
<T: Equatable>
T must conform to protocol
Multiple Constraints
<T: A & B>
T must conform to both
Where Clause
where T.Element == Int
Complex constraints
Multiple Types
<T, U>
Multiple type parameters
Common Generic Functions
swap(_:_:)- Swap two valuesmin(_:_:)/max(_:_:)- Compare valuesArray.map,filter,reduceOptional.map,flatMap
Benefits
- Type Safety - Compiler checks types
- Reusability - One function for many types
- Performance - No runtime overhead
- Clarity - Explicit type relationships
main.swift
// GENERIC FUNCTIONS
// Write flexible, reusable code that works with any type
// ===== BASIC GENERIC FUNCTION =====
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 5, y = 10
swapValues(&x, &y)
print("x: \(x), y: \(y)") // x: 10, y: 5
var s1 = "Hello", s2 = "World"
swapValues(&s1, &s2)
print("s1: \(s1), s2: \(s2)") // s1: World, s2: Hello
// ===== GENERIC FUNCTION WITH RETURN =====
func firstElement<T>(of array: [T]) -> T? {
return array.first
}
let numbers = [1, 2, 3]
let first = firstElement(of: numbers) // Optional(1)
let names = ["Alice", "Bob"]
let firstName = firstElement(of: names) // Optional("Alice")
// ===== MULTIPLE TYPE PARAMETERS =====
func makePair<T, U>(_ first: T, _ second: U) -> (T, U) {
return (first, second)
}
let pair1 = makePair(1, "one") // (Int, String)
let pair2 = makePair("key", 42) // (String, Int)
let pair3 = makePair(true, 3.14) // (Bool, Double)
// ===== TYPE CONSTRAINTS =====
// Require types to conform to protocols
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, item) in array.enumerated() {
if item == value {
return index
}
}
return nil
}
let index = findIndex(of: "Bob", in: ["Alice", "Bob", "Charlie"])
print("Index: \(index ?? -1)") // Index: 1
// ===== MULTIPLE CONSTRAINTS =====
func printSorted<T: Comparable & CustomStringConvertible>(_ array: [T]) {
for item in array.sorted() {
print(item.description)
}
}
// ===== WHERE CLAUSE =====
func allEqual<T: Equatable>(_ a: T, _ b: T, _ c: T) -> Bool
where T: Comparable {
return a == b && b == c
}
print(allEqual(1, 1, 1)) // true
print(allEqual(1, 2, 1)) // false
// ===== GENERIC WITH ASSOCIATED TYPE =====
func sum<T: Sequence>(_ sequence: T) -> Int
where T.Element == Int {
return sequence.reduce(0, +)
}
print(sum([1, 2, 3, 4, 5])) // 15
print(sum(1...10)) // 55
// ===== SPECIALIZED GENERIC FUNCTIONS =====
// Works with any numeric type
func maximum<T: Comparable>(_ a: T, _ b: T) -> T {
return a > b ? a : b
}
print(maximum(5, 3)) // 5
print(maximum(3.14, 2.71)) // 3.14
print(maximum("a", "z")) // z
// ===== GENERIC ALGORITHMS =====
func filter<T>(_ array: [T], where condition: (T) -> Bool) -> [T] {
var result: [T] = []
for item in array {
if condition(item) {
result.append(item)
}
}
return result
}
let evens = filter([1, 2, 3, 4, 5]) { $0 % 2 == 0 }
print(evens) // [2, 4]
// ===== TYPE INFERENCE =====
// Swift infers T from usage
let result1 = firstElement(of: [1, 2, 3]) // T = Int
let result2 = firstElement(of: ["a", "b"]) // T = String
// Explicit type annotation
let result3: Int? = firstElement(of: [1, 2, 3])Try It Yourself!
Create a generic Stack<T> data structure with push, pop, and peek methods!