Advanced Swift Feature: Generics with Associated Types

A portrait painting style image of a pirate holding an iPhone.

by The Captain

on
April 15, 2023

Advanced Swift Feature: Generics with Associated Types

Generics in Swift are a powerful tool that allow us to write reusable code. While simple generics allow us to define a single type parameter that can be used to describe any type, associated types take it one step further by allowing us to define an abstract type that can be used as a parameter in a generic type.

Let's take a look at an example. Suppose we want to create a protocol that describes a generic stack.

    protocol Stack {
        associatedtype Element
        
        mutating func push(_ element: Element)
        mutating func pop() -> Element?
        var isEmpty: Bool { get }
        var count: Int { get }
    }

In this protocol, we define an associated type Element, which is used as the type of the elements that will be pushed and popped from the stack.

Now, let's create a generic implementation of this protocol, using an array to store the stack elements.

    struct ArrayStack: Stack {
        typealias Element = T
        
        private var elements: [Element] = []
        
        mutating func push(_ element: T) {
            elements.append(element)
        }
        
        mutating func pop() -> T? {
            return elements.popLast()
        }
        
        var isEmpty: Bool {
            return elements.isEmpty
        }
        
        var count: Int {
            return elements.count
        }
    }

In this implementation, we define the type parameter T, which represents the concrete type of the stack elements. We also indicate that the associated type Element is equal to T.

Now we can create an instance of our stack implementation and use it to push and pop elements.

    var stack = ArrayStack()
    stack.push(1)
    stack.push(2)
    stack.push(3)
    print(stack.pop()) // Output: Optional(3)
    print(stack.pop()) // Output: Optional(2)}

This example just scratches the surface of what we can do with associated types. They allow us to write generic code that is even more flexible, while keeping the type system strong and safe.