Hands-On Design Patterns with Swift
上QQ阅读APP看书,第一时间看更新

Generics, protocols, and associated types

You can also use associated types in your protocols. These associated types let you define protocols that are generics, like this: RunnableWithResult. We can implement a bunch of logic and code around the run() method, without actually knowing anything about the return types. We'll encounter this construction many times in this book, so it's important that you're comfortable with associate types:

protocol RunnableWithResult {
associatedtype ResultType
func run() -> ResultType
}

struct RunnersWithResult<T>: RunnableWithResult where T: RunnableWithResult {
let runnables: [T]
func run() -> [T.ResultType] {
return runnables.map { $0.run() }
}
}

Like with generic types, you can't mix and match heterogeneous types. The following example will not compile; later in this book, you'll see strategies for overcoming this common problem when dealing with generics:

struct IntRunnable {
func run() -> Int {
return 0
}
}

struct StringRunnable {
func run() -> String {
return "OK"
}
}

let runnables: [RunnableWithResult] = [StringRunnable(), IntRunnable()]

This will yield the following dreaded error:

Protocol 'RunnableWithResult' can only be used as a generic constraint because it has Self or associated type requirements