Generics

Posted on September 28, 2016


Generic Functions

The actual type to use in place of T will be determined each time the swapTwoValues(::) function is called.

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

You can provide more than one type parameter by writing multiple type parameter names within the angle brackets, separated by commas.

Always give type parameters upper camel case names (such as T and MyTypeParameter) to indicate that they are a placeholder for a type, not a value.

Generic Types

In addition to generic functions, Swift enables you to define your own generic types. These are custom classes, structures, and enumerations that can work with any type, in a similar way to Array and Dictionary.

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

Because it is a generic type, Stack can be used to create a stack of any valid type in Swift, in a similar manner to Array and Dictionary.

var stackOfStrings = Stack<String>()

When extending a generic type, you do not provide a type parameter list but use the same from the original type definition:

extension Stack {
    var topItem: Element? {
        return items.isEmpty ? nil : items[items.count - 1]
    }
}

Associated Types

An associated type gives a placeholder name to a type that is used as part of the protocol.

protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

And in the class/struct that conforms to the Container protocol:

// conformance to the Container protocol
typealias ItemType = Int

A generic where clause enables you to require that an associated type must conform to a certain protocol, or that certain type parameters and associated types must be the same:

func allItemsMatch<C1: Container, C2: Container>
    (_ someContainer: C1, _ anotherContainer: C2) -> Bool
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
            
        // Check that both containers contain the same number of items.
        if someContainer.count != anotherContainer.count {
            return false
        }
            
        // Check each pair of items to see if they are equivalent.
        for i in 0..<someContainer.count {
            if someContainer[i] != anotherContainer[i] {
                return false
            }
        }
        
        // All items match, so return true.
        return true
}

Stay in touch!