01. Summary of the keyword 'associatedtype'

It will take about 3 minutes to finish reading this article.

In Swift, When defining a protocol, we can’t use generics in a protocol as in a class. The following code is incorrect and will be reported as an error.

Example Code

1
2
3
4
5
6
protocol Stackble <Element> { 
mutating func push(_ element:Element)
mutating func pop()->Element
func top() ->Element
func size() ->Int
}

Therefore, associated types can solve this problem in Swift. It’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that’s used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.

Example Code

1
2
3
4
5
6
7
protocol Stackble {    
associatedtype Element
mutating func push(_ element:Element)
mutating func pop()->Element
func top() ->Element
func size() ->Int
}

Associated types can be applied in situations as follows:

1. Associated type be replaced by concrete type

Example Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class StringStack: Stackble {
//typealias String = Element
var elements = [String]()
func push(_ element:String){

elements.append(element)
}
func pop()->String{
elements.removeLast()
}
func top() ->String{
elements.last!
}
func size() ->Int{
elements.count
}
}

Thanks to Swift’s type inference, we don’t actually need to declare a concrete Element of String as part of the definition of StringStack.

2. Associated type be replaced by generic type

In a class with generics, generic types replace association types.

Example Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Stack <E>: Stackble {
//typealias E = Element
var elements = [E]()
func push(_ element:E) {
elements.append(element)
}
func pop()->E{
elements.removeLast()
}
func top() ->E{
elements.last!
}
func size() ->Int{
elements.count
}
}

3. Points for Attention

A protocol contains associated types cannot be used as return values and function parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 protocol Runnable {
// without any associated types
}
class Person : Runnable {

}
class Car : Runnable {

}
func get(_ type:Int) -> Runnable {
if(0 == type) {
return Person()
}
return Car()
}
//call as follows:
var r1 = get(0)
var r2 = get(1)
print("r1=",r1)
print("r2=",r2)
//everything is ok now

The following is the code of compilation error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protocol Runnable {
associatedtype Speed
var speed : Speed {get}

}
class Person:Runnable {
var speed: Double = 0.0
}
class Car:Runnable {
var speed: Double = 0.0
}

// this code will be reported an error
func get (run: Runnable) {}
// this code will be reported an error too.
func get(_ type:Int) -> Runnable {
if(0 == type ){
return Person()
}
return Car()
}

We can fix this point just by a generic Type that conform to the protocol.

1
2
3
4
5
6
7
func get<T:Runnable>(_ type:Int)-> T {  
if 0 == type {
let result = Person() as! T
return result
}
return Car() as! T
}

Reference

[1] https://docs.swift.org/swift-book/LanguageGuide/Generics.html
[2] https://blog.csdn.net/boildoctor/article/details/113116245