Swift Generics

Swift generics let you write flexible, reusable functions and types that work with any type while preserving full type safety.

What Are Generics?

Without generics, you'd need a separate function for every type you want to support — one to swap two integers, another to swap two strings, and so on. Generics let you write the logic once using a placeholder type, conventionally named T, and the compiler fills in the real type at each call site.

Swap Two Values

func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temporary = a
    a = b
    b = temporary
}

var first = 10
var second = 42
swapValues(&first, &second)
print(first, second) // 42 10

var nameOne = "Alice"
var nameTwo = "Bob"
swapValues(&nameOne, &nameTwo)
print(nameOne, nameTwo) // Bob Alice

Writing Generic Functions

A generic function can take multiple type parameters, and you can constrain them to protocols so the compiler knows which operations are legal. Below, T is restricted to Equatable, which guarantees the == operator exists for whatever concrete type is used.

Generic Search Function

func firstIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
    for (index, element) in array.enumerated() {
        if element == value {
            return index
        }
    }
    return nil
}

let numbers = [5, 12, 8, 19, 3]
if let position = firstIndex(of: 19, in: numbers) {
    print("Found at index \(position)") // Found at index 3
}

let words = ["swift", "kotlin", "rust"]
print(firstIndex(of: "rust", in: words) ?? -1) // 2

Generic Types

Generics aren't limited to functions — structs, classes, and enums can also be generic. A generic Stack<Element> works identically whether Element is Int, String, or a custom model type, and the compiler still enforces that you never mix types within one instance.

A Generic Stack

struct Stack<Element> {
    private var items: [Element] = []

    mutating func push(_ item: Element) {
        items.append(item)
    }

    mutating func pop() -> Element? {
        return items.popLast()
    }

    var top: Element? {
        return items.last
    }

    var isEmpty: Bool {
        return items.isEmpty
    }
}

var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
intStack.push(3)
print(intStack.pop() ?? 0) // 3

var stringStack = Stack<String>()
stringStack.push("first")
stringStack.push("second")
print(stringStack.top ?? "") // second

Constraining Type Parameters

  • Equatable — allows values of T to be compared with == and !=
  • Comparable — enables ordering with <, >, <=, and >=
  • Hashable — lets T be stored in a Set or used as a Dictionary key
  • Numeric — restricts T to numeric types so arithmetic operators are available
AspectConcrete TypeGeneric Type
ReusabilityWorks with one type onlyWorks with any qualifying type
Type SafetyFullFull
Code DuplicationOne function per typeA single implementation
Example Signaturefunc addInts(a: Int, b: Int) -> Intfunc add<T: Numeric>(a: T, b: T) -> T
Note: Give generic placeholders meaningful names like Element or Key instead of a bare T when it improves readability, especially in public APIs — Swift's own standard library does this throughout.
Note: The compiler specializes generic code for every concrete type it's used with, so generics rarely cost performance — but stacking too many constraints on one type parameter can make error messages difficult to read.

Exercise: Swift Generics

What core problem do generics solve in Swift?