13

Golang 泛型初探

 3 years ago
source link: https://www.purewhite.io/2021/03/09/golang-generic-glance/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Golang 泛型初探

发表于 2021-03-09 分类于 go 阅读次数:126 Disqus:0 Comments
本文字数: 4.1k 阅读时长 ≈ 8 分钟

Golang 的泛型实现已经正式合并到 master 分支上啦,之后也会在 master 分支上进行开发,那么作为期待这个 feature 许久的 gopher,也想第一时间看看到底是如何实现的。

这里不过多讲解泛型的语法,具体可以参考一下 https://github.com/golang/go/issues/43651 这个 issue。

简单来说,在 struct 和 func 的名字后面可以加一个 [] 里面包含泛型的名字和限制条件,比如:

type container[T any] struct{
elem T
}

any 是个特殊的关键字,表示所有类型都可以。

这里我们写一个示例程序来编译成汇编,来看看泛型到底是怎么实现的:

package main

type Stringer interface {
String() string
}

type Stringer2 interface {
Stringer
}

type container[T Stringer] struct {
s T
}

type stringerImpl struct {
s string
}

func (s stringerImpl) String() string {
return s.s
}

func loop[T any](s []T) {
for _, v := range s {
_ = v
}
}

func main() {
loop([]int{1, 2, 3, 4, 5})

c := container[Stringer2]{}
loop([]container[Stringer2]{c})
}

编译成汇编

我们先基于 master 分支来编译一个 go 出来,然后用这个 go 来执行以下命令:

$ go build -gcflags="-G=3 -l -S" main.go > main.s 2>&1

接下来去 main.s 这个文件看看,就会发现有这么一段代码:

"".#loop[int] STEXT nosplit size=18 args=0x18 locals=0x0 funcid=0x0
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:39) TEXT "".#loop[int](SB), NOSPLIT|ABIInternal, $0-24
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:39) FUNCDATA $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:39) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:33) MOVQ "".s+16(SP), AX
0x0005 00005 (/Users/purewhite/go/src/local/study/main.go:33) XORL CX, CX
0x0007 00007 (/Users/purewhite/go/src/local/study/main.go:33) JMP 12
0x0009 00009 (/Users/purewhite/go/src/local/study/main.go:33) INCQ CX
0x000c 00012 (/Users/purewhite/go/src/local/study/main.go:33) CMPQ AX, CX
0x000f 00015 (/Users/purewhite/go/src/local/study/main.go:33) JGT 9
0x0011 00017 (/Users/purewhite/go/src/local/study/main.go:33) RET
0x0000 48 8b 44 24 10 31 c9 eb 03 48 ff c1 48 39 c8 7f H.D$.1...H..H9..
0x0010 f8 c3 ..
"".#loop[container[Stringer2]] STEXT nosplit size=21 args=0x18 locals=0x0 funcid=0x0
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:44) TEXT "".#loop[container[Stringer2]](SB), NOSPLIT|ABIInternal, $0-24
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:44) FUNCDATA $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:44) FUNCDATA $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
0x0000 00000 (/Users/purewhite/go/src/local/study/main.go:33) MOVQ "".s+16(SP), AX
0x0005 00005 (/Users/purewhite/go/src/local/study/main.go:33) TESTQ AX, AX
0x0008 00008 (/Users/purewhite/go/src/local/study/main.go:33) JLE 20
0x000a 00010 (/Users/purewhite/go/src/local/study/main.go:33) XORL CX, CX
0x000c 00012 (/Users/purewhite/go/src/local/study/main.go:33) INCQ CX
0x000f 00015 (/Users/purewhite/go/src/local/study/main.go:33) CMPQ AX, CX
0x0012 00018 (/Users/purewhite/go/src/local/study/main.go:33) JGT 12
0x0014 00020 (/Users/purewhite/go/src/local/study/main.go:33) RET
0x0000 48 8b 44 24 10 48 85 c0 7e 0a 31 c9 48 ff c1 48 H.D$.H..~.1.H..H
0x0010 39 c8 7f f8 c3 9....

再看 main 中调用的地方:

	0x008c 00140 (/Users/purewhite/go/src/local/study/main.go:39)	CALL	"".#loop[int](SB)
...
0x00c0 00192 (/Users/purewhite/go/src/local/study/main.go:44) CALL "".#loop[container[Stringer2]](SB)

基本可以确定,go 的泛型目前的实现方案是在编译时进行代码生成,这个方案虽然会降低编译速度,但是在运行时是没有性能损耗的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK