golang 1.18版本有很大的变化,增加了泛型支持

泛型

golang1.18就加入泛型,通过泛型的支持,将减少很多代码量,但也会带来不少可读性问题,因此泛型在生产中仍然需要谨慎使用,特别是目前golang的泛型还比较基础。

golang 泛型使用[]定义

1
2
3
func print[v int | uint | uint32](params v) {
fmt.Println(params)
}

声明泛型类型

上面的函数支持了三种类型[v int | uint | string],那如果我们要支持更多的类型,岂不是该函数会非常的长,并且不好维护,达不到不用的效果,例如其他的函数也要支持这样的类型。这时候可以声明泛型类型,格式如下:

1
2
3
type Nums interface {
int | uint64 | uint32
}

函数修改为

1
2
3
func print[v Nums](params v) {
fmt.Println(params)
}

这样把Nums当作函数的入参,调用方也不需要调整,这样如果后续需要支持更多的类型,只需要在Nums内添加即可。

当然此时Nums只支持基础类型,而如果对基础类型进行引用就不支持了,例如我们声明类型type myInt32 int32,就无法使用.

这时候可以将Nums进行简单改造即可,在int32前面添加~表示支持int32的所有类型:

1
2
3
type Nums interface {
int | uint64 | uint32 | ~int32
}

关键字any

any类型,该类型其实就是空接口interface{}的别名。同时新增一个comparable类型表示可比较类型,包括bool、number、string、pointer、channel等可比较的类型

1
2
3
func Hello[v any](params v) {
fmt.Println(params)
}
1
2
3
4
5
6
7
8
9
10
func Hello[v comparable](params v) {
fmt.Println(params)
}

c := struct {
Age int
}{
1,
}
Hello(c)

fuzzing测试

1.18新增单元测试fuzzing,其目的是通过随机生成的数据进行测试,找出单元测试(unit test)覆盖不到的场景,进而发现潜在的BUG和安全漏洞。

使用说明

  • fuzz测试的函数名称必须是FuzzXXX格式,并且入参是a *testing.F类型,且函数无返回值
  • fuzz测试与常规的单元测试一样,其测试文件必须以_test.go结尾
  • fuzz方法支持的参数:string, bool,[]byte,int, int8, int16, int32/rune,int64,uint, uint8/byte, uint16, uint32, uint64,float32, float64
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
func Reverse(s string) string {
b := []byte(s)
for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}

func TestReverse(t *testing.T) {
testcases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{" ", " "},
{"!12345", "54321!"},
}
for _, tc := range testcases {
rev := Reverse(tc.in)
if rev != tc.want {
t.Errorf("Reverse: %q, want %q", rev, tc.want)
}
}
}

func FuzzReverse(f *testing.F) {
testcases := []string{"Hello, world", " ", "!12345"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}

f.Fuzz(func(t *testing.T, orig string) {
rev := Reverse(orig)
doubleRev := Reverse(rev)
if orig != doubleRev {
t.Errorf("Before: %q, after: %q", orig, doubleRev)
}
if utf8.ValidString(orig) && !utf8.ValidString(rev) {
t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
}
})
}

运行fuzz测试

1
go test -fuzz=FuzzReverse

提示如下信息

1
2
3
4
5
6
7
--- FAIL: FuzzReverse (0.02s)
--- FAIL: FuzzReverse (0.00s)
1.18_test.go:112: Reverse produced invalid UTF-8 string "\xac\xde"

Failing input written to testdata/fuzz/FuzzReverse/d10bbca0ed89c568
To re-run:
go test -run=FuzzReverse/d10bbca0ed89c568

查看提示文件

1
2
3
4
# vi  testdata/fuzz/FuzzReverse/d10bbca0ed89c568

go test fuzz v1
string("ެ")

执行

1
2
3
4
5
6
go test -run=FuzzReverse/d10bbca0ed89c568

# 提示
--- FAIL: FuzzReverse (0.00s)
--- FAIL: FuzzReverse/d10bbca0ed89c568 (0.00s)
1.18_test.go:112: Reverse produced invalid UTF-8 string "\xac\xde"

fuzz模糊测试的作用是非常巨大的,可以发现我们程序中潜在的BUG,特别是人为的测试用例是无法覆盖完全的,借助fuzz可以起到很好的辅助作用

官方命令变化

go get命令已弃用,并且被go install替代
go work

新增work工作空间,类似与go.mod,不过这个工作空间可以用于切换本地与远程的调试。

参考文章

参考文章Golang1.18新特性