目录

go 1.18 重大版本更新个人解读

新版本加入泛型、提升20%性能、增加工作空间概念、支持模糊测试

关注1.18版本都快半年了
220315 终于正式发布go1.18版本
虽然个人意愿不太支持增加泛型这种语法功能
但新版本发布还是有点兴奋和迫不及待

参考:

  1. 官方文档 go official doc about go1.18
  2. 官方博文 go official blog about go1.18
  3. 语法说明 go language specification (已添加1.18语法细节)
  4. 官方推荐阅读 讨论泛型文章 Type Parameters Proposal

更新环境变量和工具

更新了环境变量后,试着直接把自己原来项目升级生1.18版本

1
2
go mod tidy
go mod tidy: go.mod file indicates go 1.18, but maximum supported version is 1.17

看来直接修改是不行的

找了一下原因,后面定位到vscode工具的使用上
把设置里面的go.path改成了新的路径
把系统环境变量的GOROOT GOPATH 都改了

改了以后就能运行了……所以问题出在?
问题出在自己没logout再登录(linux mint改了 ~/.profile文件以后要重新登录里面的变量才能生效,自己的go配置全放在这个文件夹里),所以vscode的工具应该是没问题的

ok, 项目能跑了,继续在里面跑泛型代码熟悉一下新语法

小结

  1. 更改环境变量 GOROOT GOPATH 全部改成1.18版本的文件夹路径(自己新建的)
  2. 更改 vscode 设置 go.path 改成1.18版本路径
  3. logout一下系统,再重进,确保环境变量更新
  4. 重新登录 vscode ,会有提示,重新构建 go 需要的语法静态检测工具

泛型 ( generic )

初探

新建一个文件夹写测试

1
2
3
4
mkdir testgo1.18
cd testgo1.18/
go mod init testgo1.18
vi 1_test.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 1_test.go
package generic

import (
	"fmt"
	"testing"
)

func Print[T any](s []T) { 
	for _, v := range s {
		fmt.Println(v)
	}
}

func Test2203161216(t *testing.T) {
	Print([]int{1,2,3})
	Print([]byte{'s', 't', 'u'})
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
go test 1_test.go -v
# 结果如下
=== RUN   Test2203161216
1
2
3
115
116
117
--- PASS: Test2203161216 (0.00s)
PASS
ok  	command-line-arguments	0.002s

1
2
3
4
# 目录
testgo1.18/
├── 1_test.go
└── go.mod

到此为止算是用过go1.18的泛型了,接下来学习更详细的语法细节

泛型语法详细

官方总结得比较复杂,只有一篇很长的21年8月发布的讨论泛型的文章(里面介绍了大部分语法,虽然时隔半年才正式发布,但完全兼容里面的内容),和 go language specification (文档很长,泛型语法比较分散)
建议阅读 go language specification ( 已经是1.18最新版 )
paul 看完后总结如下

1.18新增符号 ~

官方解释:
The type set of a term of the form ~T is the set of types whose underlying type is T.
~T 指:所有以 T 为基础的类型(可以理解成继承 T 的类型,或以 T 为父类型的类型)

注意:
In a term of the form ~T, the underlying type of T must be itself, and T cannot be an interface
如果用了 ~T 的话,那 T 必须是基础类型(不可以继承自基础类型),而且 T 不可以是接口

1
2
3
4
5
6
7
type MyInt int

interface {
	~[]byte  // the underlying type of []byte is itself
	~MyInt   // 非法 illegal: the underlying type of MyInt is not MyInt
	~error   // 非法 illegal: error is an interface
}

Union elements

1
2
3
4
5
6
// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
type Float interface {
	~float32 | ~float64
}

Float可以表示所有以 float32 和 float64为基础类型的 类型(包含这两个)
注意:

  1. 所有 union elements 之间不可以有交际 a | b | c (a, b, c 三种类型必须没有交集)
  2. 不能嵌套
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 错误示范
interface {
	P                // illegal: P is a type parameter
	int | P          // illegal: P is a type parameter
	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
	float32 | Float  // overlapping type sets but Float is an interface
}
// illegal: Bad3 cannot embed a union containing Bad3
type Bad3 interface {
	~int | ~string | Bad3
}

新增标准库 constraints

地址 https://pkg.go.dev/constraints
源码很简单,就几行
看到例子是用在函数声明或者类型声明的时候
比如想要在声明的函数里面比较泛类型 T 的话,要加限制(因为不是所有的基础类型都能比较的)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Smallest returns the smallest element in a slice.
// It panics if the slice is empty.
func Smallest[T constraints.Ordered](s []T) T {
	r := s[0] // panics if slice is empty
	for _, v := range s[1:] {
		if v < r { // 如果没加限制声明的时候是 [T any] 的话,此行编译时报错
			r = v
		}
	}
	return r
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 以下是 constraints.Orderd 的源码
type Ordered interface {  // 涵盖了所有数字类型 和 所有数字类型的子类型
	Integer | Float | ~string
}
type Integer interface {
	Signed | Unsigned
}
type Float interface {
	~float32 | ~float64
}
type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}
type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

map匹配的格式

[]map[int]bool 可以匹配下面所有类型:

1
2
3
4
[]map[int]bool  // 毫无疑问
T1              // (T1 匹配 []map[int]bool)  
[]T1            //(T1 匹配 map[int]bool)  
[]map[T1]T2     //(T1 匹配 int, T2 匹配 bool)  

[]map[int]bool 不能匹配下面所有类型:

1
2
3
4
int              // 毫无疑问
[]map[T1]string  // 毫无疑问
struct{}         // 需要注意,虽然map底层是结构体,但不匹配这个
[]struct{}       

泛型使用场景

当然内容远不止上面那么几条,文章 里面写得很清楚了,如果要深入学习使用泛型的话这篇文章要多看几遍,暂时没有需求,还有以下几块知识点本文不再总结了

  1. 泛型 + 接口
  2. 泛型 + 函数
  3. 泛型 + 指针

GopherCon 2021年大会go作者有提到go泛型使用场景,不建议乱用泛型
演讲视频: https://www.youtube.com/watch?v=Pa_e9EeCdy8
go作者建议的泛型使用场景:

  1. 对于 slice、map、channel 等类型,如果它们的元素类型是不确定的,操作这类类型的函数可以考虑用泛型
  2. 一些通用目的的数据结构,比如前面提到的二叉树等
  3. 如果一些函数行为相同,只是类型不同,可以考虑用泛型重构

go.work go workspace

不改变原来 go mod 的使用
添加了一个工作空间的概念,一个工作空间里面可以有多个go mod,每个go mod 只能有一个mian包和main函数
语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Usage:

        go work <command> [arguments]

The commands are:

        edit        edit go.work from tools or scripts
        init        initialize workspace file
        sync        sync workspace build list to modules
        use         add modules to workspace file
Use "go help work <command>" for more information about a command.

模糊测试 Fuzzing

什么是模糊测试 fuzzing ?
常用于处理有用户输入的场景,如果主动停止它会一直测试下去,直到发现程序异常

模糊测试 (fuzz testing, fuzzing)是一种软件测试技术。其核心思想是将自动或半自动生成的随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏。模糊测试常常用于检测软件或计算机系统的安全漏洞。

go fuzzing 大概介绍,参考 https://mp.weixin.qq.com/s/UqjSA2i3s1VoLFACt_EL2A

工具和标准库的更新

go tools 和 go std pkg 都有更新,参考 https://go.dev/doc/go1.18
下面列举一些自己觉得重要的

  1. gofmt 支持并发,现在在多核处理器上gofmt的性能会有较大提升
  2. 增加 go work
  3. go get 默认加 -d 参数(只下载源码不安装),要安装的话 go install
  4. sync 包增加 Mutex.TryLock (一个复杂的话题,不建议使用此方法)