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)
}
associatedtype Item
func add(_ item: Item)
}
Key Concepts
Type Inference
Swift infers the type from implementation
typealias Item = IntConstraints
Restrict associated type
associatedtype Item: EquatableWhere Clauses
Add complex constraints
where Item == Other.Itemsome/any Keywords
Use protocols as types
func get() -> some PUsing 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 typeany Container - Existential type, can be any conforming typemain.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!