Protocols and Generics in Swift

  • Protocols can only be used as a generic constraint because they have Self or associated type requirements.
  • Protocols do not allow the use of generic parameters, but instead use associated types.
  • Specialization cannot be applied to non-generic types.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112


import Foundation

protocol BaseReq {
// Generic Type T in protocol
associatedtype T
func obj(_ s:String) -> T
init(completion:((_ res: Result<T>)->())?)
}

extension BaseReq {
func launch() {
let s = "network return string"
obj(s)
}
}

class AReq: BaseReq {
// declare the Type of T
typealias T = AObj
required init(completion:((_ res: Result<T>)->())?) {

}

func obj(_ s: String) -> T {
return T(s)
}
}

class BReq: BaseReq {
typealias T = BObj
required init(completion:((_ res: Result<T>)->())?) {

}

func obj(_ s:String) -> T {
return T(s)
}
}

class BaseObj {

}

class AObj: BaseObj {

var a = "a"

init(_ s: String) {
print("a", s)
}
}

class BObj: BaseObj {

var b = "b"

init(_ s: String) {
print("b", s)
}
}

class Launch {

// static func launch(req: BaseReq) -> BaseReq {
// If you write like the one above, you will get:
// Error: Protocol 'BaseReq' can only be used as a generic constraint because it has Self or associated type requirements.
static func launch<U: BaseReq>(req: U) -> U {
let s = "network return string"
print(req)

req.obj(s)

return req
}
}

// Cannot specialize non-generic type
// enum Result {
enum Result<T> {
case suc(T)
case err(Error)
}


let areq = AReq { (r) in
switch r {
case .suc(let s):
// S is AObj, we can access s.a
s.a
case .err(let r):
break
}
}
areq.launch()
// or
Launch.launch(req: areq)

let breq = BReq { (r) in
switch r {
case .suc(let s):
// S is BObj, we can access s.b
s.b
case .err(let r):
break
}
}
breq.launch()
Launch.launch(req: breq)
s

Translated by gpt-3.5-turbo