【Go】泛型

要求:go 1.18及以上版本。

基本语法

基本语法是在函数名后用方括号指定泛型参数,即func 函数名[泛型参数](函数参数){}。例如我们可以实现一个泛型版的max函数。

func max[T int | float64](a, b T) T {
    
    
  if a > b {
    
    
    return a
  }
  return b
}

上面的max函数就是一个泛型版的函数,泛型参数为T,它可以是intfloat64类型,我们用|(或)来分隔它们。类型参数的语义和函数参数是一致的,都是参数名 类型的形式,而且|连接符也是符合经验直觉的,这是Go语言的一个优点,简单且符合经验直觉,所以接受起来非常顺畅。不像Haskell那样,学起来非常晦涩,比如它的函子,应用函子,单子是在直接向你灌输,不管你理不理解,接不接受,就是硬来,当然这并不能怪它,因为它的抽象级别实在是太高了。

Go的泛型函数在调用时可以指定泛型类型,它的位置也是在函数名之后,参数之前,即函数名[泛型类型](参数列表);也可以省略泛型类型,前提是编译器能够推断出具体的类型。例如:

func main() {
    
    
  m1 := max[int](1, 3)
  
  a, b := 1, 3
  m2 := max(a, b)
  
  m3 := max(1.2, 1.4)
  
  fmt.Println("m1:", m1, "\nm2:", m2, "\nm3:", m3)
}

除了|语法,泛型中还增加了~语法,它用在类型之前,~T表示任何底层类型为T的类型。举个例子,~int除了支持int类型,还支持任何通过type xxx int定义的类型。还是以上面的max为例,修改如下:

func max[T ~int | float64](a, b T) T {
    
    
  if a > b {
    
    
    return a
  }
  return b
}

type myInt int
type myFloat float64

func main() {
    
    
  a, b := myInt(1), myInt(2)
  m1 := max(a, b) //正确
  fmt.Println("m:", m1)

  c, d := myFloat(1.1), myFloat(2.2)
  m2 := max(c, d) //会报错
  fmt.Println("m2:", m2)
}

注意|~的区别,前者是连接类型,后者是对类型本身的修饰。

当然我们的max还不够通用,我们想让max能处理更多的类型,但是又不想函数写的太“难看”。好在Go支持将接口作为泛型类型,我们可以定义一个Ord接口类型,表示可排序的类型。

type Ord interface {
    
    
  byte | int8 | int16 | int32 | int64 | int | float32 | float64 | string
}

func max[T Ord](a, b T) T {
    
    
  if a > b {
    
    
    return a
  }
  return b
}

这样就好多了,现在我们的max可以支持9种类型了,如果需要支持更多的类型,我们只需要调整Ord接口的定义就好了,max不用再改变。

func main() {
    
    
  var a, b byte = 1, 2
  m1 := max(a, b)

  m2 := max("abc", "abd")

  fmt.Println("m1:", m1, "\nm2:", m2)
}

其实Go官方也有一个Ordered的接口类型,表示可排序的类型,它在constraints包下,不过这个包在Go 1.18被移出了标准包,放到了golang.org/x/exp下,可以点击这里查看。

如果你了解过Haskell的话,看到这里应该会觉得有些熟悉。Haskell中的max函数类型如下:

Prelude> :t max
max :: Ord a => a -> a -> a

在Haskell中实现max函数如下:

扫描二维码关注公众号,回复: 14780478 查看本文章
max' :: Ord a => a -> a -> a
max' a b = if a > b then a else b

Haskell中的Ord是类型类,表示类型的类型,是对类型的抽象,可以理解为接口。Haskell提供了非常丰富的类型抽象以及强大的类型推断系统,如果代码能通过编译,那么就没有bug,这可不是吹的。

说回Go,在Go的内置类型中,有一个comparable类型,在builtin.go中定义:

// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, arrays of comparable types,
// structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable interface{
    
     comparable }

这里我们并没有用它来定义max函数,因为内置的comparable类型表示的是支持==!=比较的类型,并没有对><的支持,所以无法用来定义max函数,只能实现eq这样的功能。comparable主要还是用于map的键。

func eq[T comparable](a, b T) bool {
    
    
  return a == b
}

func main() {
    
    
  fmt.Println(eq("ab", "ab"))
}

既然接口能作为泛型类型,那么普通接口自然也不例外,我们可以自定义一个“可比较的”接口。

type myComparable interface {
    
    
  compare(c myComparable) int
}

由于是自定义的接口,内置类型并没有实现这个接口,因此我们需要自己实现。

type myInt int

func (i myInt) compare(c myComparable) int {
    
    
  v, _ := c.(myInt) //示例代码,所以并不严格
  return int(i) - int(v)
}

接下来就可以用myComparable来定义max函数了。

func max[T myComparable](a, b T) T {
    
    
  if a.compare(b) > 0 {
    
    
    return a
  }
  return b
}

func main() {
    
    
  a, b := myInt(1), myInt(2)
  m := max(a, b)
  fmt.Println("m:", m)
}

如果不出意外的话,那就是没有意外,你会看到m: 2的输出。

实现原理

关于Go泛型的实现原理,官方并没有给出具体文档。网上对此也有讨论,不过都语焉不详。我也不能给出详细的说明,只是从汇编的角度带大家来探索一下。

Go的泛型应该是在编译阶段为每种shape生成了一套不同的代码。shape是类型在内存中的表现形式,不同的类型,shape可能相同,也可能不同。比如intfloat32的shape就不同,而inttype Int int的shape就相同。

来看第一个例子:

package main

func max[T int | float64](a, b T) T {
    
    
  if a > b {
    
    
    return a
  }
  return b
}

func main() {
    
    
  max(1, 2)
  max(1.1, 2.2)
}

使用go tool compile -N -l -S -C -K fanxing.go > _test.s输出汇编代码。

"".main STEXT size=87 args=0x0 locals=0x20 funcid=0x0 align=0x0
  0x0000 00000 (fanxing.go:10)  TEXT  "".main(SB), ABIInternal, $32-0
  0x0000 00000 (fanxing.go:10)  CMPQ  SP, 16(R14)
  0x0004 00004 (fanxing.go:10)  JLS  80
  0x0006 00006 (fanxing.go:10)  SUBQ  $32, SP
  0x000a 00010 (fanxing.go:10)  MOVQ  BP, 24(SP)
  0x000f 00015 (fanxing.go:10)  LEAQ  24(SP), BP
  0x0014 00020 (fanxing.go:11)  LEAQ  ""..dict.max[int](SB), AX //类型参数
  0x001b 00027 (fanxing.go:11)  MOVL  $1, BX //参数a
  0x0020 00032 (fanxing.go:11)  MOVL  $2, CX //参数b
  0x0025 00037 (fanxing.go:11)  CALL  "".max[go.shape.int_0](SB) //max[int](1,2)
  0x002a 00042 (fanxing.go:12)  LEAQ  ""..dict.max[float64](SB), AX //类型参数
  0x0031 00049 (fanxing.go:12)  MOVSD  $f64.3ff199999999999a(SB), X0 //参数a
  0x0039 00057 (fanxing.go:12)  MOVSD  $f64.400199999999999a(SB), X1 //参数b
  0x0041 00065 (fanxing.go:12)  CALL  "".max[go.shape.float64_0](SB) //max[float64](1.1,2.2)
  0x0046 00070 (fanxing.go:13)  MOVQ  24(SP), BP
  0x004b 00075 (fanxing.go:13)  ADDQ  $32, SP
  0x004f 00079 (fanxing.go:13)  RET
  0x0050 00080 (fanxing.go:13)  NOP
  0x0050 00080 (fanxing.go:10)  CALL  runtime.morestack_noctxt(SB)
  0x0055 00085 (fanxing.go:10)  JMP  0
  0x0000 49 3b 66 10 76 4a 48 83 ec 20 48 89 6c 24 18 48  I;f.vJH.. H.l$.H
  0x0010 8d 6c 24 18 48 8d 05 00 00 00 00 bb 01 00 00 00  .l$.H...........
  0x0020 b9 02 00 00 00 e8 00 00 00 00 48 8d 05 00 00 00  ..........H.....
  0x0030 00 f2 0f 10 05 00 00 00 00 f2 0f 10 0d 00 00 00  ................
  0x0040 00 e8 00 00 00 00 48 8b 6c 24 18 48 83 c4 20 c3  ......H.l$.H.. .
  0x0050 e8 00 00 00 00 eb a9                             .......
  rel 23+4 t=14 ""..dict.max[int]+0
  rel 38+4 t=7 "".max[go.shape.int_0]+0
  rel 45+4 t=14 ""..dict.max[float64]+0
  rel 53+4 t=14 $f64.3ff199999999999a+0
  rel 61+4 t=14 $f64.400199999999999a+0
  rel 66+4 t=7 "".max[go.shape.float64_0]+0
  rel 81+4 t=7 runtime.morestack_noctxt+0
"".max[go.shape.int_0] STEXT dupok nosplit size=89 args=0x18 locals=0x10 funcid=0x0 align=0x0
  0x0000 00000 (fanxing.go:3)  TEXT  "".max[go.shape.int_0](SB), DUPOK|NOSPLIT|ABIInternal, $16-24
  0x0000 00000 (fanxing.go:3)  SUBQ  $16, SP   //SP-16
  0x0004 00004 (fanxing.go:3)  MOVQ  BP, 8(SP) //push BP
  0x0009 00009 (fanxing.go:3)  LEAQ  8(SP), BP //BP=SP
  0x000e 00014 (fanxing.go:3)  MOVQ  AX, ""..dict+24(SP) //类型参数
  0x0013 00019 (fanxing.go:3)  MOVQ  BX, "".a+32(SP)     //参数a
  0x0018 00024 (fanxing.go:3)  MOVQ  CX, "".b+40(SP)     //参数b
  0x001d 00029 (fanxing.go:3)  MOVQ  $0, "".~r0(SP)      //返回值
  0x0025 00037 (fanxing.go:4)  MOVQ  "".a+32(SP), CX
  0x002a 00042 (fanxing.go:4)  CMPQ  "".b+40(SP), CX
  0x002f 00047 (fanxing.go:4)  JLT  51
  0x0031 00049 (fanxing.go:4)  JMP  70
  0x0033 00051 (fanxing.go:5)  MOVQ  "".a+32(SP), AX //a>b
  0x0038 00056 (fanxing.go:5)  MOVQ  AX, "".~r0(SP)  //返回a
  0x003c 00060 (fanxing.go:5)  MOVQ  8(SP), BP //pop BP
  0x0041 00065 (fanxing.go:5)  ADDQ  $16, SP   //SP+16
  0x0045 00069 (fanxing.go:5)  RET
  0x0046 00070 (fanxing.go:7)  MOVQ  "".b+40(SP), AX //a<=b
  0x004b 00075 (fanxing.go:7)  MOVQ  AX, "".~r0(SP)  //返回b
  0x004f 00079 (fanxing.go:7)  MOVQ  8(SP), BP //pop BP
  0x0054 00084 (fanxing.go:7)  ADDQ  $16, SP   //SP+16
  0x0058 00088 (fanxing.go:7)  RET
  0x0000 48 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 89  H...H.l$.H.l$.H.
  0x0010 44 24 18 48 89 5c 24 20 48 89 4c 24 28 48 c7 04  D$.H.\$ H.L$(H..
  0x0020 24 00 00 00 00 48 8b 4c 24 20 48 39 4c 24 28 7c  $....H.L$ H9L$(|
  0x0030 02 eb 13 48 8b 44 24 20 48 89 04 24 48 8b 6c 24  ...H.D$ H..$H.l$
  0x0040 08 48 83 c4 10 c3 48 8b 44 24 28 48 89 04 24 48  .H....H.D$(H..$H
  0x0050 8b 6c 24 08 48 83 c4 10 c3                       .l$.H....
"".max[go.shape.float64_0] STEXT dupok nosplit size=101 args=0x18 locals=0x10 funcid=0x0 align=0x0
  0x0000 00000 (fanxing.go:3)  TEXT  "".max[go.shape.float64_0](SB), DUPOK|NOSPLIT|ABIInternal, $16-24
  0x0000 00000 (fanxing.go:3)  SUBQ  $16, SP
  0x0004 00004 (fanxing.go:3)  MOVQ  BP, 8(SP)
  0x0009 00009 (fanxing.go:3)  LEAQ  8(SP), BP
  0x000e 00014 (fanxing.go:3)  MOVQ  AX, ""..dict+24(SP)
  0x0013 00019 (fanxing.go:3)  MOVSD  X0, "".a+32(SP)
  0x0019 00025 (fanxing.go:3)  MOVSD  X1, "".b+40(SP)
  0x001f 00031 (fanxing.go:3)  XORPS  X1, X1
  0x0022 00034 (fanxing.go:3)  MOVSD  X1, "".~r0(SP)
  0x0027 00039 (fanxing.go:4)  MOVSD  "".b+40(SP), X1
  0x002d 00045 (fanxing.go:4)  MOVSD  "".a+32(SP), X2
  0x0033 00051 (fanxing.go:4)  UCOMISD  X1, X2
  0x0037 00055 (fanxing.go:4)  JHI  59
  0x0039 00057 (fanxing.go:4)  JMP  80
  0x003b 00059 (fanxing.go:5)  MOVSD  "".a+32(SP), X0
  0x0041 00065 (fanxing.go:5)  MOVSD  X0, "".~r0(SP)
  0x0046 00070 (fanxing.go:5)  MOVQ  8(SP), BP
  0x004b 00075 (fanxing.go:5)  ADDQ  $16, SP
  0x004f 00079 (fanxing.go:5)  RET
  0x0050 00080 (fanxing.go:7)  MOVSD  "".b+40(SP), X0
  0x0056 00086 (fanxing.go:7)  MOVSD  X0, "".~r0(SP)
  0x005b 00091 (fanxing.go:7)  MOVQ  8(SP), BP
  0x0060 00096 (fanxing.go:7)  ADDQ  $16, SP
  0x0064 00100 (fanxing.go:7)  RET
  0x0000 48 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 89  H...H.l$.H.l$.H.
  0x0010 44 24 18 f2 0f 11 44 24 20 f2 0f 11 4c 24 28 0f  D$....D$ ...L$(.
  0x0020 57 c9 f2 0f 11 0c 24 f2 0f 10 4c 24 28 f2 0f 10  W.....$...L$(...
  0x0030 54 24 20 66 0f 2e d1 77 02 eb 15 f2 0f 10 44 24  T$ f...w......D$
  0x0040 20 f2 0f 11 04 24 48 8b 6c 24 08 48 83 c4 10 c3   ....$H.l$.H....
  0x0050 f2 0f 10 44 24 28 f2 0f 11 04 24 48 8b 6c 24 08  ...D$(....$H.l$.
  0x0060 48 83 c4 10 c3                                   H....
""..dict.max[int] SRODATA dupok size=8
  0x0000 00 00 00 00 00 00 00 00                          ........
  rel 0+8 t=1 type.int+0
  rel 0+0 t=23 type.int+0
""..dict.max[float64] SRODATA dupok size=8
  0x0000 00 00 00 00 00 00 00 00                          ........
  rel 0+8 t=1 type.float64+0
  rel 0+0 t=23 type.float64+0
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
  0x0000 6d 61 69 6e                                      main
""..inittask SNOPTRDATA size=24
  0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0x0010 00 00 00 00 00 00 00 00                          ........
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
  0x0000 01 00 00 00 00 00 00 00                          ........
"".max[go.shape.int_0].arginfo1 SRODATA static dupok size=5
  0x0000 08 08 10 08 ff                                   .....
"".max[go.shape.float64_0].arginfo1 SRODATA static dupok size=5
  0x0000 08 08 10 08 ff                                   .....
$f64.3ff199999999999a SRODATA size=8
  0x0000 9a 99 99 99 99 99 f1 3f                          .......?
$f64.400199999999999a SRODATA size=8
  0x0000 9a 99 99 99 99 99 01 40                          .......@

内容比较多,我们删除了PCDATAFUNCDATA相关的语句,它们是编译器自动插入的GC相关的信息。

TEXT "".main(SB)是我们的main函数,其中有两条CALL指令:

  • CALL "".max[go.shape.int_0](SB)
  • CALL "".max[go.shape.float64_0](SB)

编译器为intfloat64分别生成了不同的函数代码,从实现中可以看到其实传递了3个参数:

  • AX:类型信息
  • BX:参数a
  • CX:参数b

Go编译器输出的汇编和我们手写的汇编有很大区别,不必纠结为什么是这样,而且我们删掉了一些内容,所以这些偏移量不是那么直观。另外,我们自己写汇编会用到伪FP寄存器加正偏移来访问参数和返回值,通过伪SP寄存器加负偏移来访问局部变量,但是编译器输出的汇编都是通过伪SP寄存器访问的。大家可以结合注释稍微看下,float64版本的maxint版差不多,只是改用了浮点数相关指令。

类型信息虽然作为参数传入,但这里似乎没有使用。

来看第二个例子:

package main

type myInt int

func max[T int | myInt](a, b T) T {
    
    
  if a > b {
    
    
    return a
  }
  return b
}

func main() {
    
    
  max(1, 2)
  max(myInt(1), myInt(2))
}

输出汇编代码如下:

"".main STEXT size=86 args=0x0 locals=0x20 funcid=0x0 align=0x0
  0x0000 00000 (fanxing.go:12)  TEXT  "".main(SB), ABIInternal, $32-0
  0x0000 00000 (fanxing.go:12)  CMPQ  SP, 16(R14)
  0x0004 00004 (fanxing.go:12)  JLS  79
  0x0006 00006 (fanxing.go:12)  SUBQ  $32, SP
  0x000a 00010 (fanxing.go:12)  MOVQ  BP, 24(SP)
  0x000f 00015 (fanxing.go:12)  LEAQ  24(SP), BP
  0x0014 00020 (fanxing.go:13)  LEAQ  ""..dict.max[int](SB), AX //类型参数
  0x001b 00027 (fanxing.go:13)  MOVL  $1, BX //参数a
  0x0020 00032 (fanxing.go:13)  MOVL  $2, CX //参数b
  0x0025 00037 (fanxing.go:13)  CALL  "".max[go.shape.int_0](SB) //max[int](1,2)
  0x002a 00042 (fanxing.go:14)  LEAQ  ""..dict.max["".myInt](SB), AX //类型参数
  0x0031 00049 (fanxing.go:14)  MOVL  $1, BX //参数a
  0x0036 00054 (fanxing.go:14)  MOVL  $2, CX //参数b
  0x003b 00059 (fanxing.go:14)  NOP
  0x0040 00064 (fanxing.go:14)  CALL  "".max[go.shape.int_0](SB) //max[myInt](myInt(1),myInt(2))
  0x0045 00069 (fanxing.go:15)  MOVQ  24(SP), BP
  0x004a 00074 (fanxing.go:15)  ADDQ  $32, SP
  0x004e 00078 (fanxing.go:15)  RET
  0x004f 00079 (fanxing.go:15)  NOP
  0x004f 00079 (fanxing.go:12)  CALL  runtime.morestack_noctxt(SB)
  0x0054 00084 (fanxing.go:12)  JMP  0
  0x0000 49 3b 66 10 76 49 48 83 ec 20 48 89 6c 24 18 48  I;f.vIH.. H.l$.H
  0x0010 8d 6c 24 18 48 8d 05 00 00 00 00 bb 01 00 00 00  .l$.H...........
  0x0020 b9 02 00 00 00 e8 00 00 00 00 48 8d 05 00 00 00  ..........H.....
  0x0030 00 bb 01 00 00 00 b9 02 00 00 00 0f 1f 44 00 00  .............D..
  0x0040 e8 00 00 00 00 48 8b 6c 24 18 48 83 c4 20 c3 e8  .....H.l$.H.. ..
  0x0050 00 00 00 00 eb aa                                ......
  rel 23+4 t=14 ""..dict.max[int]+0
  rel 38+4 t=7 "".max[go.shape.int_0]+0
  rel 45+4 t=14 ""..dict.max["".myInt]+0
  rel 65+4 t=7 "".max[go.shape.int_0]+0
  rel 80+4 t=7 runtime.morestack_noctxt+0
"".max[go.shape.int_0] STEXT dupok nosplit size=89 args=0x18 locals=0x10 funcid=0x0 align=0x0
  0x0000 00000 (fanxing.go:5)  TEXT  "".max[go.shape.int_0](SB), DUPOK|NOSPLIT|ABIInternal, $16-24
  0x0000 00000 (fanxing.go:5)  SUBQ  $16, SP    //SP-16
  0x0004 00004 (fanxing.go:5)  MOVQ  BP, 8(SP)  //push BP
  0x0009 00009 (fanxing.go:5)  LEAQ  8(SP), BP  //BP=SP
  0x000e 00014 (fanxing.go:5)  MOVQ  AX, ""..dict+24(SP) //类型参数
  0x0013 00019 (fanxing.go:5)  MOVQ  BX, "".a+32(SP)     //参数a
  0x0018 00024 (fanxing.go:5)  MOVQ  CX, "".b+40(SP)     //参数b
  0x001d 00029 (fanxing.go:5)  MOVQ  $0, "".~r0(SP)      //返回值
  0x0025 00037 (fanxing.go:6)  MOVQ  "".a+32(SP), CX
  0x002a 00042 (fanxing.go:6)  CMPQ  "".b+40(SP), CX
  0x002f 00047 (fanxing.go:6)  JLT  51
  0x0031 00049 (fanxing.go:6)  JMP  70
  0x0033 00051 (fanxing.go:7)  MOVQ  "".a+32(SP), AX //a>b
  0x0038 00056 (fanxing.go:7)  MOVQ  AX, "".~r0(SP)  //返回a
  0x003c 00060 (fanxing.go:7)  MOVQ  8(SP), BP //pop BP
  0x0041 00065 (fanxing.go:7)  ADDQ  $16, SP   //SP+16
  0x0045 00069 (fanxing.go:7)  RET
  0x0046 00070 (fanxing.go:9)  MOVQ  "".b+40(SP), AX //a<=b
  0x004b 00075 (fanxing.go:9)  MOVQ  AX, "".~r0(SP)  //返回b
  0x004f 00079 (fanxing.go:9)  MOVQ  8(SP), BP //pop BP
  0x0054 00084 (fanxing.go:9)  ADDQ  $16, SP   //SP+16
  0x0058 00088 (fanxing.go:9)  RET
  0x0000 48 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 89  H...H.l$.H.l$.H.
  0x0010 44 24 18 48 89 5c 24 20 48 89 4c 24 28 48 c7 04  D$.H.\$ H.L$(H..
  0x0020 24 00 00 00 00 48 8b 4c 24 20 48 39 4c 24 28 7c  $....H.L$ H9L$(|
  0x0030 02 eb 13 48 8b 44 24 20 48 89 04 24 48 8b 6c 24  ...H.D$ H..$H.l$
  0x0040 08 48 83 c4 10 c3 48 8b 44 24 28 48 89 04 24 48  .H....H.D$(H..$H
  0x0050 8b 6c 24 08 48 83 c4 10 c3                       .l$.H....
""..dict.max[int] SRODATA dupok size=8
  0x0000 00 00 00 00 00 00 00 00                          ........
  rel 0+8 t=1 type.int+0
  rel 0+0 t=23 type.int+0
""..dict.max["".myInt] SRODATA dupok size=8
  0x0000 00 00 00 00 00 00 00 00                          ........
  rel 0+8 t=1 type."".myInt+0
  rel 0+0 t=23 type."".myInt+0
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
  0x0000 6d 61 69 6e                                      main
""..inittask SNOPTRDATA size=24
  0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0x0010 00 00 00 00 00 00 00 00                          ........
runtime.memequal64·f SRODATA dupok size=8
  0x0000 00 00 00 00 00 00 00 00                          ........
  rel 0+8 t=1 runtime.memequal64+0
runtime.gcbits.01 SRODATA dupok size=1
  0x0000 01                                               .
type..namedata.*main.myInt- SRODATA dupok size=13
  0x0000 00 0b 2a 6d 61 69 6e 2e 6d 79 49 6e 74           ..*main.myInt
type.*"".myInt SRODATA dupok size=56
  0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00  ................
  0x0010 54 a0 11 18 08 08 08 36 00 00 00 00 00 00 00 00  T......6........
  0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0x0030 00 00 00 00 00 00 00 00                          ........
  rel 24+8 t=1 runtime.memequal64·f+0
  rel 32+8 t=1 runtime.gcbits.01+0
  rel 40+4 t=5 type..namedata.*main.myInt-+0
  rel 48+8 t=1 type."".myInt+0
runtime.gcbits. SRODATA dupok size=0
type."".myInt SRODATA dupok size=64
  0x0000 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0x0010 3a be a9 f0 0f 08 08 02 00 00 00 00 00 00 00 00  :...............
  0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  0x0030 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00  ................
  rel 24+8 t=1 runtime.memequal64·f+0
  rel 32+8 t=1 runtime.gcbits.+0
  rel 40+4 t=5 type..namedata.*main.myInt-+0
  rel 44+4 t=5 type.*"".myInt+0
  rel 48+4 t=5 type..importpath."".+0
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
  0x0000 01 00 00 00 00 00 00 00                          ........
"".max[go.shape.int_0].arginfo1 SRODATA static dupok size=5
  0x0000 08 08 10 08 ff                                   .....

注意main函数中的两条CALL指令都是调用的"".max[go.shape.int_0](SB),因为intmyInt的shape是相同的,所以编译器只生成了一个函数。

另外泛型函数都有DUPOK标记,它其实是dup ok,表示可以有多个重复的实例,由链接器保留其中一个实例,丢弃其他重复的实例。

附录

泛型入门:https://go.dev/doc/tutorial/generics

Go汇编:https://golang.google.cn/doc/asm

猜你喜欢

转载自blog.csdn.net/puss0/article/details/128257153