|
@@ -0,0 +1,238 @@
|
|
1
|
+# Go generic
|
|
2
|
+Qinghe
|
|
3
|
+21 Jan 2022
|
|
4
|
+
|
|
5
|
+## Defining constraints
|
|
6
|
+constraints的作用:
|
|
7
|
+- 规定一些类型参数可以调用的方法
|
|
8
|
+
|
|
9
|
+interface即可满足上面的需求,因此继续沿用interface类型来定义一个constraint。
|
|
10
|
+
|
|
11
|
+还是以Stringify为例,定义一个constraint:
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+.code codes/stringify.go /STRINGER OMIT/,/STRINGER OMIT/
|
|
15
|
+
|
|
16
|
+- 与接口定义一样,定义一些方法集
|
|
17
|
+- 类型参数必须实现interface
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+## any constraints
|
|
21
|
+
|
|
22
|
+- any是一个constraint,表示类型参数可以是任何类型
|
|
23
|
+- constraint是个interface类型,类型参数必须实现interface
|
|
24
|
+
|
|
25
|
+推断出
|
|
26
|
+
|
|
27
|
+.code codes/any.go /ANY OMIT/,/ANY OMIT/
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+**any的本质是个空的interface**
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+- any一开始被设计为只允许用在泛型中的类型约束上,后续经社区讨论修改后可代替interface{}用在任何地方 [issue#33232](https://github.com/golang/go/issues/33232)
|
|
34
|
+- any是个预定义类型,和int、string等类似
|
|
35
|
+- 可以被覆盖,不会影响兼容性
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+## Using a constraint
|
|
39
|
+
|
|
40
|
+- 单个类型参数
|
|
41
|
+
|
|
42
|
+.code codes/use_constraints.go /STR OMIT/,/STR OMIT/
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+## Using a constraint
|
|
46
|
+多个类型参数
|
|
47
|
+
|
|
48
|
+- 相同约束
|
|
49
|
+
|
|
50
|
+.code codes/use_constraints.go /PRT OMIT/,/PRT OMIT/
|
|
51
|
+
|
|
52
|
+- 不同约束
|
|
53
|
+
|
|
54
|
+.code codes/use_constraints.go /CONCAT OMIT/,/CONCAT OMIT/
|
|
55
|
+
|
|
56
|
+## Generic types
|
|
57
|
+
|
|
58
|
+## Generic types
|
|
59
|
+
|
|
60
|
+不光有泛型函数,也有泛型类型,在定义一些通用类型时可以用到。
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+.code codes/generic_stack.go /STACK OMIT/,/STACK OMIT/
|
|
64
|
+
|
|
65
|
+在使用泛型类型时,必须提供类型实参,这一操作被称为「类型实例化」。
|
|
66
|
+
|
|
67
|
+.code codes/generic_stack.go /INTSTACK OMIT/,/INTSTACK OMIT/
|
|
68
|
+
|
|
69
|
+其他使用和普通类型没有区别
|
|
70
|
+
|
|
71
|
+.code codes/generic_stack.go /TYPE OMIT/,/TYPE OMIT/
|
|
72
|
+
|
|
73
|
+## Generic types
|
|
74
|
+泛型类型和一般类型一样,也可以定义方法。
|
|
75
|
+
|
|
76
|
+.code codes/generic_stack.go /METHOD OMIT/,/METHOD OMIT/
|
|
77
|
+
|
|
78
|
+- 声明方法时,receiver类型的类型参数**必须**与声明receiver类型时定义的参数**个数相同**
|
|
79
|
+- 方法的类型参数实参名称和类型定义时的类型参数形参**名称不需要相同**,在方法中没有使用到类型时,名称可以为 "_"
|
|
80
|
+- 类型参数后面**没有**constraints
|
|
81
|
+
|
|
82
|
+## Generic types
|
|
83
|
+方法的声明中,不能包含类型参数。以下代码均编译不通过:
|
|
84
|
+
|
|
85
|
+.play codes/method.go /START OMIT/,/END OMIT/
|
|
86
|
+
|
|
87
|
+至于原因,请见后续。
|
|
88
|
+
|
|
89
|
+## Operators
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+## Operators
|
|
93
|
+由前面的例子可以看出,使用interface作为constraints时,只规定了一系列可调用的方法集。
|
|
94
|
+到目前为止,泛型方法能够做的事情,除了任何类型都能做的事情外,也就只有
|
|
95
|
+方法调用了。
|
|
96
|
+
|
|
97
|
+但是方法调用并不能够完美实现某些需求。如下:
|
|
98
|
+
|
|
99
|
+.code codes/operator.go /MIN OMIT/,/MIN OMIT/
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+不是所有的类型都支持`<`操作符,而且`<`不是一个方法,无法通过interface来实现。
|
|
103
|
+
|
|
104
|
+## Operators
|
|
105
|
+
|
|
106
|
+- 除了两种例外情况(后面会将),所有的四则运算、大小比较、逻辑运算只允许被用在预定义类型或者底层为预定义类型的自定义类型中。
|
|
107
|
+- instead of saying which operators a constraint should support, we can say which types a constraint should accept. We do this by defining a type set for a constraint.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+## Type sets
|
|
111
|
+
|
|
112
|
+## Type sets
|
|
113
|
+
|
|
114
|
+todo
|
|
115
|
+
|
|
116
|
+## Constraint elements
|
|
117
|
+在interface类型中额外定义一些元素作为约束,这些元素包括:
|
|
118
|
+
|
|
119
|
+- 具体类型
|
|
120
|
+- 近似类型
|
|
121
|
+- 联合类型
|
|
122
|
+
|
|
123
|
+## Arbitrary type constraint element
|
|
124
|
+
|
|
125
|
+可以在interface中嵌入任意类型
|
|
126
|
+
|
|
127
|
+.code codes/type_sets.go /TYPE OMIT/,/TYPE OMIT/
|
|
128
|
+
|
|
129
|
+但是,类型不能是type parameter
|
|
130
|
+
|
|
131
|
+.code codes/type_sets.go /INVALID OMIT/,/INVALID OMIT/
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+## Approximation constraint element(近似类型)
|
|
135
|
+
|
|
136
|
+**定义类型和非定义类型(defined type and undefined type)**
|
|
137
|
+
|
|
138
|
+一个定义类型是一个在某个类型定义声明中定义的类型。
|
|
139
|
+
|
|
140
|
+所有的基本类型都是定义类型。一个非定义类型一定是一个组合类型。
|
|
141
|
+
|
|
142
|
+**底层类型(underlying type)**
|
|
143
|
+
|
|
144
|
+在Go中,每个类型都有一个底层类型。规则:
|
|
145
|
+- 一个内置基本类型的底层类型为它自己。
|
|
146
|
+- unsafe标准库包中定义的Pointer类型的底层类型是它自己。(至少我们可以认为是这样。事实上,关于unsafe.Pointer类型的底层类型,官方文档中并没有清晰的说明。我们也可以认为unsafe.Pointer类型的底层类型为*T,其中T表示一个任意类型。)
|
|
147
|
+- 一个非定义类型(必为一个组合类型)的底层类型为它自己。
|
|
148
|
+- 在一个类型声明中,新声明的类型和源类型共享底层类型。
|
|
149
|
+
|
|
150
|
+来源: [go101#类型系统概述](https://gfw.go101.org/article/type-system-overview.html)
|
|
151
|
+
|
|
152
|
+## Approximation constraint element
|
|
153
|
+
|
|
154
|
+如果某个类型T1,底层类型为类型T2,则这个类型T1被称为类型T2的近似类型。一个类型的近似类型可以有多个,这些所有的近似类型集合可以
|
|
155
|
+用符号`~`来表示。
|
|
156
|
+
|
|
157
|
+例如:
|
|
158
|
+
|
|
159
|
+.code codes/type_sets.go /APPRO OMIT/,/APPRO OMIT/
|
|
160
|
+
|
|
161
|
+约束AnyInteger表示的类型集为所有底层类型为int的类型,其中包括MyInt类型,因此MyInt可以作为type argument参数。
|
|
162
|
+
|
|
163
|
+因为~T表示所有底层类型为T的类型,当然类型T的底层类型也应该是自己,满足这个条件的类型有:
|
|
164
|
+- 非定义类型
|
|
165
|
+- 大部分预定义类型,比如int或者string(不包括error类型)
|
|
166
|
+
|
|
167
|
+## Approximation constraint element
|
|
168
|
+在近似类型中,类型T不能为类型参数或者interface类型。
|
|
169
|
+
|
|
170
|
+.code codes/type_sets.go /ALL INVALID OMIT/,/ALL INVALID OMIT/
|
|
171
|
+
|
|
172
|
+## Union constraint element(联合类型)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+## Union constraint element
|
|
176
|
+第三种允许使用的约束元素是**联合类型**。这是一种新的语法结构,用竖线(|)将一系列约束元素分隔。联合类型所代表的类型集是各约束元素类型集的并集,并且各
|
|
177
|
+约束元素不可以重复。
|
|
178
|
+
|
|
179
|
+.code codes/type_sets.go /ONION EXAMPLE OMIT/,/ONION EXAMPLE OMIT/
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+## Operations based on type sets
|
|
183
|
+
|
|
184
|
+提出type sets的目的就是允许泛型函数使用'<'、'>'之类的运算符。
|
|
185
|
+
|
|
186
|
+泛型函数中允许的操作是type sets中所有的类型都允许的操作。因此,一般来说,**约束中
|
|
187
|
+包括的类型越多,那么允许的操作就越少,包括的类型越少,则允许的操作就越多**。
|
|
188
|
+
|
|
189
|
+## Operations based on type sets
|
|
190
|
+
|
|
191
|
+回过头再来看之前的`Min`函数的例子,可以使用如下的约束:
|
|
192
|
+
|
|
193
|
+.code codes/operator.go /OPERATOR CONSTRAINT OMIT/,/OPERATOR CONSTRAINT OMIT/
|
|
194
|
+
|
|
195
|
+现在,下面的代码就可以运行了
|
|
196
|
+
|
|
197
|
+.code codes/operator.go /OPERATOR MIN OMIT/,/OPERATOR MIN OMIT/
|
|
198
|
+
|
|
199
|
+## Comparable types in constraints
|
|
200
|
+
|
|
201
|
+之前我们说,操作符只能被用于语言预定义的类型或底层类型为语言预定义类型的类型之间,但是有两个例外,这两个例外就是`==`和`!=`。
|
|
202
|
+
|
|
203
|
+这两个操作符允许被用在struct、array和接口类型之间。但是我们无法定义一个约束,用来包含这些所有可以进行比较的类型。
|
|
204
|
+
|
|
205
|
+## Comparable types in constraints
|
|
206
|
+
|
|
207
|
+因此,设计了一个内置的约束:`comparable`。这个约束所包含的类型集为所有可以进行`==`和`!=`操作的类型。
|
|
208
|
+
|
|
209
|
+例如:
|
|
210
|
+
|
|
211
|
+.code codes/comparable.go /COMPARABLE EXAMPLE OMIT/,/ COMPARABLE EXAMPLE OMIT/
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+## Comparable types in constraints
|
|
215
|
+
|
|
216
|
+comparable作为一个约束,它也可以被嵌入在其他约束内。
|
|
217
|
+
|
|
218
|
+.code codes/comparable.go /ComparableHasher OMIT/,/ComparableHasher OMIT/
|
|
219
|
+
|
|
220
|
+满足上面约束的类型必须满足两个条件:
|
|
221
|
+- 必须可以比较
|
|
222
|
+- 必须有Hash() uintptr方法
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+## Mutually referencing type parameters(相互引用类型参数)
|
|
227
|
+
|
|
228
|
+在一个泛型函数的定义中,类型参数之间可能会相互引用。
|
|
229
|
+
|
|
230
|
+例如,要实现图数据结构的一些基本算法。图包括两种结构,`Node`和`Edge`,`Node`应该有个方法Egdes()[]Edge,用于返回和节点相连的边。Edge也有个方法
|
|
231
|
+Nodes()(Node,Node),用于返回一条边两端连接的节点。一个完整的图就可以用[]Node来表示。
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+## Mutually referencing type parameters
|
|
235
|
+
|
|
236
|
+代码如下:
|
|
237
|
+
|
|
238
|
+.code codes/graph.go /GRAPH OMIT/,/GRAPH OMIT/
|