Lesson 74Protocols

Associated Types

What are Associated Types?

Associated types are placeholder types within a protocol. They're like generics for protocols - the conforming type decides what concrete type to use!

Syntax

protocol Container {
    associatedtype Item
    func add(_ item: Item)
}

Key Concepts

Type Inference

Swift infers the type from implementation

typealias Item = Int

Constraints

Restrict associated type

associatedtype Item: Equatable

Where Clauses

Add complex constraints

where Item == Other.Item

some/any Keywords

Use protocols as types

func get() -> some P

Using Protocols with Associated Types

Can't use directly as parameter type:

// func f(_ c: Container) { } ❌
func f<C: Container>(_ c: C) { } ✓
func f(_ c: any Container) { } ✓

some vs any

some Container - Opaque type, compiler knows exact type
any Container - Existential type, can be any conforming type
main.swift
// ASSOCIATED TYPES
// Placeholder types in protocols - like generics for protocols

// BASIC ASSOCIATED TYPE
protocol Container {
    associatedtype Item  // Placeholder for a type
    var count: Int { get }
    mutating func append(_ item: Item)
    subscript(i: Int) -> Item { get }
}

// Implementing with specific type
struct IntStack: Container {
    typealias Item = Int  // Optional: Swift can infer this

    var items: [Int] = []

    var count: Int {
        return items.count
    }

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

    subscript(i: Int) -> Int {
        return items[i]
    }
}

// Implementing with different type
struct StringStack: Container {
    var items: [String] = []

    var count: Int { items.count }

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

    subscript(i: Int) -> String {
        return items[i]
    }
}

// ASSOCIATED TYPE WITH CONSTRAINTS
protocol ComparableContainer {
    associatedtype Item: Comparable
    var items: [Item] { get }
    func sorted() -> [Item]
}

struct NumberBox: ComparableContainer {
    var items: [Int]

    func sorted() -> [Int] {
        return items.sorted()
    }
}

// MULTIPLE ASSOCIATED TYPES
protocol KeyValueStore {
    associatedtype Key: Hashable
    associatedtype Value

    subscript(key: Key) -> Value? { get set }
    var count: Int { get }
}

struct StringIntStore: KeyValueStore {
    private var storage: [String: Int] = [:]

    subscript(key: String) -> Int? {
        get { storage[key] }
        set { storage[key] = newValue }
    }

    var count: Int { storage.count }
}

// USING PROTOCOLS WITH ASSOCIATED TYPES
// Can't use directly as type - use generics instead

// This WON'T work:
// func processContainer(_ container: Container) { }  // Error!

// Use generic constraint instead:
func processContainer<C: Container>(_ container: C) {
    print("Container has \(container.count) items")
}

// Or use some/any keywords (Swift 5.7+)
func getAnyContainer() -> any Container {
    return IntStack(items: [1, 2, 3])
}

func getSomeContainer() -> some Container {
    return IntStack(items: [1, 2, 3])
}

// PRACTICAL EXAMPLE: Iterator Pattern
protocol IteratorProtocol {
    associatedtype Element
    mutating func next() -> Element?
}

struct CountdownIterator: IteratorProtocol {
    var count: Int

    mutating func next() -> Int? {
        if count <= 0 {
            return nil
        }
        defer { count -= 1 }
        return count
    }
}

var countdown = CountdownIterator(count: 5)
while let num = countdown.next() {
    print(num)  // 5, 4, 3, 2, 1
}

// EXAMPLE: Repository Pattern
protocol Repository {
    associatedtype Entity
    associatedtype ID: Hashable

    func find(by id: ID) -> Entity?
    func save(_ entity: Entity)
    func delete(by id: ID)
}

struct User {
    let id: Int
    var name: String
}

class UserRepository: Repository {
    private var users: [Int: User] = [:]

    func find(by id: Int) -> User? {
        return users[id]
    }

    func save(_ entity: User) {
        users[entity.id] = entity
    }

    func delete(by id: Int) {
        users.removeValue(forKey: id)
    }
}

Try It Yourself!

Create a Queue protocol with an associated type Element. Implement it with IntQueue and StringQueue structs!