Generics

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Generics let a function or type be written once and used with any type that satisfies the requirements it states, instead of duplicating the same logic per concrete type or giving up type safety by falling back to Any. They are the mechanism behind almost every standard-library type already used throughout this reference — Array<Element>, Optional<Wrapped>, Dictionary<Key, Value> — and behind the protocol-oriented patterns in Protocols.

The Problem Generics Solve

func swapInts(_ a: inout Int, _ b: inout Int) {
    let temporary = a
    a = b
    b = temporary
}

func swapStrings(_ a: inout String, _ b: inout String) {   // identical body, only the type differs
    let temporary = a
    a = b
    b = temporary
}

Without generics, swapping two Int values and swapping two String values require two separate functions with identical bodies — the logic has nothing to do with Int or String specifically, only the shape of "two values of the same type" matters. Generic code names that shape once with a placeholder type and lets the compiler generate (or dynamically witness) the concrete version for every type actually used, with the same compile-time type checking a non-generic function gets.

Generic Functions and Types

func swapValues<T>(_ a: inout T, _ b: inout T) {    // one function replaces swapInts and swapStrings both
    let temporary = a
    a = b
    b = temporary
}

var x = 3
var y = 7
swapValues(&x, &y)                                   // T is inferred as Int at the call site

struct Stack<Element> {                              // a generic type: Element is a placeholder, not a real type
    private var items: [Element] = []

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

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

var integerStack = Stack<Int>()                      // Stack instantiated with Element = Int
integerStack.push(1)
var stringStack = Stack<String>()                    // a separate instantiation with Element = String
stringStack.push("ready")

A generic function declares one or more type parameters in angle brackets right after its name (<T> above); each type parameter stands in for a real type that isn’t known until the function is called, and every use of that parameter within the function’s signature and body must agree with each other (both a and b are T, so swapValues can never be called with an Int and a String). A generic type — a struct, class, or enumeration — declares its type parameters the same way right after its name; each concrete type used at the point of instantiation (Stack<Int>, Stack<String>) produces its own independent version of the type, sharing one implementation but never mixing their stored data.

How a generic Stack<Element> is instantiated for Int and String, producing two independent, type-safe stacks from one implementation.

Type Parameters, Naming, and Extending Generic Types

func firstAndLast<T, U>(_ pair: (first: T, second: U)) -> (T, U) {   // multiple, independently-named parameters
    (pair.first, pair.second)
}

extension Stack {                          // extending a generic type: no parameter list repeated here
    var topItem: Element? {                // the extension's members still see Element as the type it stands for
        items.last
    }
}

extension Stack where Element: Equatable {          // an extension can add its own constraint (see below)
    func isTop(_ item: Element) -> Bool {
        topItem == item
    }
}

A type parameter’s name is arbitrary, but convention favors a single uppercase letter (T, U, V) when the parameter is a placeholder with no more specific meaning, and a descriptive UpperCamelCase name (Element, Key, Value, RawValue) when it plays a specific, well-known role — exactly as the standard library names Array’s `Element and Dictionary’s `Key/Value. Extending a generic type never repeats its type-parameter list (extension Stack { }, not extension Stack<Element> { }); the original parameter names are already in scope for every member the extension adds, and the extension itself can attach a further constraint with where that only some instantiations of the type satisfy.

Type Constraints

func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {   // T must conform to Equatable
    for (index, value) in array.enumerated() where value == valueToFind {   // == requires Equatable
        return index
    }
    return nil
}

class Named {}
func onlyClasses<T: Named>(_ value: T) {}          // a class constraint, instead of a protocol constraint

An unconstrained type parameter (plain <T>) supports only operations every type supports — assignment, passing around — since the compiler cannot assume anything else about it. A type constraint, written <T: SomeProtocol> or <T: SomeClass>, requires every type substituted for T to conform to that protocol (or inherit from that class), which is what lets findIndex use == (an Equatable requirement) in its body at all — exactly the same constraint syntax used on an associated type, covered next.

Associated Types, with Constraints and where

protocol Container {
    associatedtype Item: Equatable          // an associated type can itself carry a constraint
    var count: Int { get }
    mutating func append(_ item: Item)
    subscript(index: Int) -> Item { get }
}

extension Stack: Container {}               // Item is inferred as Element from the members Stack already has

func allItemsMatch<C1: Container, C2: Container>(_ container1: C1, _ container2: C2) -> Bool
    where C1.Item == C2.Item                // a same-type requirement relating the two associated types
{
    guard container1.count == container2.count else { return false }
    for index in 0..<container1.count where container1[index] != container2[index] {
        return false
    }
    return true
}

An associatedtype gives a protocol its own generic-like placeholder: a conforming type supplies the actual type (often inferred automatically, as Stack’s `Item is inferred to be Element above) rather than the caller choosing it as with a generic parameter. An associated type can carry its own constraint (Item: Equatable), and a where clause after a function’s parameter list can further constrain how two generic types' associated types relate to each other — C1.Item == C2.Item is a same-type requirement, satisfiable only when both containers hold the same kind of item.

Generic where Clauses: Declarations, Extensions, and Contextual Positions

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Element: Equatable, T.Element == U.Element     // where clause on the declaration itself
{
    for lhsItem in lhs {
        for rhsItem in rhs where lhsItem == rhsItem {      // "contextual" where: filters a for-in loop
            return true
        }
    }
    return false
}

extension Stack where Element: Equatable {                 // where clause on an extension (seen above too)
    func contains(_ item: Element) -> Bool {
        items.contains(item)
    }
}

protocol SuffixableContainer: Container {
    associatedtype Suffix: SuffixableContainer where Suffix.Item == Item   // where clause on an associated type
    func suffix(_ size: Int) -> Suffix
}

A where clause can appear in several positions, each restricting something different: after a generic function’s or type’s parameter list (constraining the type parameters themselves), on an extension (restricting which instantiations of a generic type the extension’s members apply to), on an associatedtype declaration (constraining the associated type in terms of the protocol’s own Self or other associated types), and contextually on a for-in loop or other pattern-matching construct, where it acts as an extra filter rather than a generic constraint at all (see Control Flow and Pattern Matching for that second, unrelated use of the same keyword).

Generic Subscripts

extension Stack {
    subscript<Indices: Sequence>(indices: Indices) -> [Element]        // a subscript with its own type parameter
        where Indices.Element == Int
    {
        indices.map { items[$0] }
    }
}

let letters = Stack<Character>()
// letters[[0, 2]] would return the elements at positions 0 and 2, generic over any Sequence of Int

A subscript can declare its own type parameters in angle brackets after subscript, independent of (and, on a generic type, in addition to) the enclosing type’s own parameters, with a where clause allowed in the same position a generic function’s would be — the plain, non-generic subscript syntax itself is covered in Methods and Subscripts.

Implicit Constraints: Copyable, and Suppressing It on a Generic Parameter

func duplicate<T>(_ value: T) -> (T, T) {     // T is implicitly constrained to Copyable -- this compiles
    (value, value)
}

func store<T: ~Copyable>(_ value: consuming T) -> [T] {   // ~Copyable suppresses the implicit constraint
    [value]                                                // a noncopyable T can appear here, but never be
}                                                           // duplicated -- only moved

Every generic type parameter implicitly requires Copyable (and Escapable) unless told otherwise, exactly like an ordinary type declaration (see Structures and Classes for ~Copyable types themselves) — which is why duplicate above can freely return two copies of T with no constraint written at all. Writing ~Copyable in a type parameter’s own constraint list, as store does, suppresses that implicit requirement for this one generic context, letting a noncopyable type be substituted for T; the tradeoff is that the generic code itself can then no longer assume it may copy a T value, only move (consuming) or borrow it.

Integer Generic Parameters (Swift 6.2)

let fixed: InlineArray<3, Int> = [1, 2, 3]      // 3 is a *value*, not a type -- an integer generic parameter
// InlineArray<count, Element>: count is fixed at compile time and is part of the type itself

Swift 6.2 extended generic parameter lists beyond types to plain integer literals: InlineArray<3, Int>’s first parameter, `3, is a compile-time integer value baked into the type itself, not a further placeholder type — InlineArray<3, Int> and InlineArray<4, Int> are as distinct as Stack<Int> and Stack<String> are. This is what lets InlineArray store its elements inline with a size fixed at compile time and no heap allocation at all; see Collections for InlineArray and Span in full.

some P as Lightweight Generic-Parameter Syntax

func sum(_ values: some Sequence<Int>) -> Int {              // equivalent to <S: Sequence<Int>>(_ values: S)
    values.reduce(0, +)
}

func genericSum<S: Sequence<Int>>(_ values: S) -> Int {      // the fully spelled-out generic equivalent
    values.reduce(0, +)
}

Writing some P directly on a parameter’s type is shorthand for an ordinary generic type parameter constrained to P, without naming that parameter at all — sum above is exactly equivalent to genericSum, just without the <S: Sequence<Int>> list, and both are still true generics: the compiler still generates (or specializes) code per concrete type substituted at the call site, and the caller still fully determines the concrete type, unlike any P. See Opaque and Boxed Protocol Types for some used as a return type (where the two cases — parameter and return position — differ in who chooses the concrete type) and for the full comparison against any.

See Also