|
@@ -1,6 +1,27 @@
|
1
|
1
|
# Go generic
|
2
|
|
-Qinghe
|
3
|
|
-21 Jan 2022
|
|
2
|
+
|
|
3
|
+清和
|
|
4
|
+2022-03-31
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+## Constraint
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+## Constraint
|
|
11
|
+
|
|
12
|
+在继续深入讨论约束之前,首先看一个特殊的constraint:**any**,表示类型参数允许是任何类型。any约束下各类型(T)允许的操作有:
|
|
13
|
+
|
|
14
|
+- 申明对应类型的变量
|
|
15
|
+- 给此类型变量重新赋一个相同类型的值
|
|
16
|
+- 将此类型变量传给其他函数或者作为返回值返回
|
|
17
|
+- 取此类型变量的地址
|
|
18
|
+- 强制类型转换为interface{}类型或者赋值给interface{}类型的变量
|
|
19
|
+- 将类型T强转为类型T(允许但是没啥用)
|
|
20
|
+- 使用类型断言将interface转换为类型T
|
|
21
|
+- 在type switch中使用此类型作为其中一个case
|
|
22
|
+- 定义及使用此类型的复合类型(例如切片)
|
|
23
|
+- 将此类型作为预定义函数的参数,比如new函数
|
|
24
|
+
|
4
|
25
|
|
5
|
26
|
## Defining constraints
|
6
|
27
|
constraints的作用:
|
|
@@ -103,15 +124,35 @@ interface即可满足上面的需求,因此继续沿用interface类型来定
|
103
|
124
|
|
104
|
125
|
## Operators
|
105
|
126
|
|
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.
|
|
127
|
+- 除了两种例外情况(后面会讲),所有的四则运算、大小比较、逻辑运算只允许被用在预定义类型或者底层为预定义类型的自定义类型中。
|
|
128
|
+- instead of saying which operators a constraint should support, we can say which types a constraint should accept.
|
|
129
|
+(与其规定一个约束能够使用哪些操作,不如规定一个约束可以接受哪些类型)
|
108
|
130
|
|
|
131
|
+这一想法可以通过定义一个约束的类型集来实现。
|
109
|
132
|
|
110
|
133
|
## Type sets
|
111
|
134
|
|
112
|
135
|
## Type sets
|
113
|
136
|
|
114
|
|
-todo
|
|
137
|
+每种类型都有相关联的类型集:
|
|
138
|
+- 对于非接口类型T,其对应的类型集为{T},集合中仅包含类型T自己
|
|
139
|
+- 对于接口类型,其对应的类型集为所有实现了接口的类型(无限个)
|
|
140
|
+
|
|
141
|
+接口内包含元素:
|
|
142
|
+1. 函数签名
|
|
143
|
+2. 其他接口
|
|
144
|
+
|
|
145
|
+.code codes/interface.go /INTERFACE OMIT/,/INTERFACE OMIT/
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+## Type sets
|
|
150
|
+
|
|
151
|
+.image type_set.png 400 600
|
|
152
|
+
|
|
153
|
+**接口类型的类型集为接口类型内各元素的类型集的交集**
|
|
154
|
+
|
|
155
|
+## Constraint elements
|
115
|
156
|
|
116
|
157
|
## Constraint elements
|
117
|
158
|
在interface类型中额外定义一些元素作为约束,这些元素包括:
|
|
@@ -194,7 +235,7 @@ todo
|
194
|
235
|
|
195
|
236
|
现在,下面的代码就可以运行了
|
196
|
237
|
|
197
|
|
-.code codes/operator.go /OPERATOR MIN OMIT/,/OPERATOR MIN OMIT/
|
|
238
|
+.code codes/operator.go /OPERATOR MIN VALID OMIT/,/OPERATOR MIN VALID OMIT/
|
198
|
239
|
|
199
|
240
|
## Comparable types in constraints
|
200
|
241
|
|
|
@@ -222,13 +263,12 @@ comparable作为一个约束,它也可以被嵌入在其他约束内。
|
222
|
263
|
- 必须有Hash() uintptr方法
|
223
|
264
|
|
224
|
265
|
|
225
|
|
-## Type inference(类型推断)
|
|
266
|
+## Type inference
|
226
|
267
|
|
227
|
268
|
## Type inference
|
228
|
|
-可以使用约束类型推断来省略一些代码。在实例化泛型函数或类型时,可以省略部分或全部类型参数。当省略的类型参数可以被推断时,如果只指定部分类型实参,
|
229
|
|
-则这部分类型实参是类型形参的第一个参数。
|
|
269
|
+在很多情况下,可以使用类型推断来避免显示指定一个或多个类型参数。可以使用**函数参数类型推断**来根据非类型参数的类型来确定类型参数的值。也可以使用**约束类型推断**来根据已知的参数类型确定其他约束的类型参数值。
|
230
|
270
|
|
231
|
|
-举例如下:
|
|
271
|
+当省略的类型参数可以被推断时,如果只指定部分类型实参,则这部分类型实参依次作为类型形参的前面部分参数值。
|
232
|
272
|
|
233
|
273
|
.code codes/type_inference.go /MAP OMIT/,/MAP OMIT/
|
234
|
274
|
|
|
@@ -330,15 +370,99 @@ comparable作为一个约束,它也可以被嵌入在其他约束内。
|
330
|
370
|
- NewPair(2, 2.5) => `compilation error`
|
331
|
371
|
|
332
|
372
|
|
|
373
|
+## Constraint type inference
|
|
374
|
+
|
|
375
|
+## Constraint type inference
|
|
376
|
+
|
|
377
|
+约束类型推断允许基于类型参数约束,根据一个类型参数实参推断出另外一个类型参数的值。
|
333
|
378
|
|
334
|
|
-## Constraint type inference (约束类型推断)
|
|
379
|
+约束类型推断有两种适用场景:
|
|
380
|
+1. 某个类型参数的约束中用到了其他类型参数
|
|
381
|
+2. 某个类型参数的约束是基于其他类型参数构成的
|
335
|
382
|
|
336
|
383
|
|
|
384
|
+约束类型推断的过程描述起来可能会比较复杂,但是通过几个典型的详细的例子,推断的过程会比较清晰。
|
337
|
385
|
|
338
|
|
-## Constraint type inference
|
339
|
386
|
|
340
|
|
-约束类型推断可以基于类型参数约束来从一个类型参数中推断另外一个类型参数的值。
|
|
387
|
+## Element constraint example
|
|
388
|
+
|
|
389
|
+例:实现一个函数,将切片内的所有元素都乘以2,返回新的切片。
|
|
390
|
+
|
|
391
|
+.code codes/constraint_inference.go /DOUBLE DEFINATION OMIT/,/DOUBLE DEFINATION OMIT/
|
|
392
|
+
|
|
393
|
+但是如果我们定义一个命名类型,然后再去调用呢?
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+.code codes/constraint_inference.go /CALL DOUBLE ERROR OMIT/,/CALL DOUBLE ERROR OMIT/
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+## Element constraint example
|
|
400
|
+引入一个新的类型参数:
|
|
401
|
+
|
|
402
|
+.code codes/constraint_inference.go /DOUBLE DEFINED OMIT/,/DOUBLE DEFINED OMIT/
|
|
403
|
+
|
|
404
|
+通过显式指定类型参数,可以得到想要的返回值类型:
|
|
405
|
+
|
|
406
|
+.code codes/constraint_inference.go /CALL DOUBLE OMIT/,/CALL DOUBLE OMIT/
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+## Element constraint example
|
|
410
|
+
|
|
411
|
+那么这个函数调用是否可以简化为以下形式呢?
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+.code codes/constraint_inference.go /CALL DOUBLE INFERENCE OMIT/,/CALL DOUBLE INFERENCE OMIT/
|
|
415
|
+
|
|
416
|
+答案是:**可以的**
|
|
417
|
+
|
|
418
|
+虽然是用函数参数类型推断无法完全推断出来,因为类型参数E没有入参与之进行归并,但是函数参数类型推断和约束类型推断结合起来,就可以了。
|
|
419
|
+
|
|
420
|
+1. 应用函数类型推断,可以得到{ S -> MySlice}
|
|
421
|
+2. 应用约束类型推断,归并[]E与MySlice,得到 {S -> MySlice, E -> int}
|
|
422
|
+3. 完成
|
|
423
|
+
|
|
424
|
+可以推断出完整调用:
|
|
425
|
+
|
|
426
|
+.code codes/constraint_inference.go /CALL DOUBLE INFERENCE WHOLE OMIT/,/CALL DOUBLE INFERENCE WHOLE OMIT/
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+## Pointer method example
|
|
431
|
+
|
|
432
|
+再看另外一个例子:
|
|
433
|
+
|
|
434
|
+.code codes/constraint_inference.go /POINTER METHOD OMIT/,/POINTER METHOD OMIT/
|
|
435
|
+
|
|
436
|
+## Pointer method example
|
|
437
|
+
|
|
438
|
+调用示例:
|
|
439
|
+
|
|
440
|
+.play -edit -numbers codes/constraint_inference2.go /POINTER METHOD CALL OMIT/,/POINTER METHOD CALL OMIT/
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+类型`Settable`并没有方法`Set(string)`,类型`*Settable`才有。
|
|
444
|
+
|
|
445
|
+`*Settable`类型零值为nil,调用Set方法会造成panic。
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+## Pointer method example
|
|
449
|
+
|
|
450
|
+我们可以将两种类型都传:
|
|
451
|
+
|
|
452
|
+.code codes/constraint_inference3.go /SETTER2 OMIT/,/SETTER2 OMIT/
|
|
453
|
+
|
|
454
|
+调用 :
|
|
455
|
+
|
|
456
|
+.play codes/constraint_inference3.go /SETTER2 CALL OMIT/,/SETTER2 CALL OMIT/
|
|
457
|
+
|
|
458
|
+## Pointer method example
|
|
459
|
+
|
|
460
|
+前面那样调用看起来会比较奇怪,因为需要重复传`Settable`参数,不过可以通过约束类型推断来减少重复工作。
|
341
|
461
|
|
342
|
|
-略
|
|
462
|
+.code codes/constraint_inference3.go /SETTER3 CALL OMIT/,/SETTER3 CALL OMIT/
|
343
|
463
|
|
|
464
|
+类型推断过程:
|
344
|
465
|
|
|
466
|
+1. {T -> Settable}
|
|
467
|
+2. {T -> Settable, PT -> *T}
|
|
468
|
+3. {T -> Settable, PT -> *Settable}
|