Golang 1.23 有什么变化
截止到目前为止,go1.23的正式版还没有发布,但是其预发布版本,也就是rc版本已经开放下载了,预发布与正式版在功能特性上是一致的,很少会发生变化。通过官网的Release Notes,可以解读到1.23有哪些新特性和提升。
官方Release Notes: https://tip.golang.org/doc/go1.23
安装:
go install golang.org/dl/go1.23rc1@latest
go1.23rc1 download
【最重要】支持使用关键字range实现用户自定义的遍历函数
range关键字在golang中一直只能遍历切片、map等内置数据结构。实际上,从1.22开始,golang就开始支持,处于实验性质,需要在运行时加上参数GOEXPERIMENT=rangefunc
,从1.23开始,在语法层面直接支持用户自定义函数(迭代器),不需要加任何参数。支持3种返回值的遍历,分别是0、1、2个返回值的遍历,函数格式分别是:
var zeroReturn func(func() bool)
var oneReturn func(func(K) bool)
var twoReturn func(func(K, V) bool)
for range zeroReturn {
}
for key := range oneReturn {
}
for key, value := range twoReturn {
}
// If break in for range call, yield return true
func twoReturnExample(yield func(index int, arr string) bool) {
for i := 0; i < 10; i++ {
if !yield(i, fmt.Sprintf("I'm %d ", i)) {
return
}
}
}
for k,v := range twoReturnExample {
if k == 2 {
break
}
fmt.Println(v)
}
// This example will print:
// I'm 0
// I'm 1
标准库中新增的标准库iter是迭代器的类型定义(就是我上面例子中的zeroReturn等几个函数)。 maps和slices中新增了很多围绕迭代器的方法(下面也会有介绍)。
官方文档:tip.golang.org/wiki/Rangef…
Demo: github.com/jin06/examp…
【比较重要】标准库中Timer和Ticker的改动
首先,Timer和Ticker的垃圾回收进行了特殊的处理,早期版本需要调用调用Stop方法才会被垃圾回收期回收,因为Timer和Ticker的实现原理是独立的goroutine定时向chan写入数据。所以如果不调用Stop方法会造成goroutine泄露,现在的优化跳过了常规的goroutine回收和chan回收,做了特殊的优化,即便不调用Stop方法,运行时也会检查并回收不用的Timer和Ticker。这个bug还是非常常见的,我就在实际工作中发现过很多同事的代码中会犯这种错误,所以这个优化还是非常有用的。
第二,Timer、Ticker改为使用非缓冲的chan。这种改动让我们写一些定时任务的时候更像linux crontab。因为很多时候Timer和Ticker都是做定时任务,任务执行如果超过了定时时间,则任务会一个接着一个做,所以这个改动更加合理,符合这两个结构体最大的应用场景需求。
【一般】新增iter标准库
因为从1.23,go正式引入用户自定义遍历函数的特性,这个包用于提供一些标准化的迭代器的结构体和方法。
【比较重要】标准库slices的改动
新增了一些方法:
All : 返回一个index,value的迭代器。(配合迭代器特性的)
Values:同All,区别只返回value的迭代器。
Backward:同All,遍历顺序相反。
Collect:将遍历函数的值写入一个slice中,传入迭代器,返回slice
func Collect[E any](seq iter.Seq[E]) []E
AppendSeq: 将迭代器的值追加到一个slice中。
Sorted: 同Collect,写入后再对slice排序。
SortedFunc: 同Sorted,增加一个排序函数作为参数。
SortedStableFunc: 同SortedFunc,使用稳定排序(稳定排序不改变元素值相同的元素的在数组中的位置,例如[1 , 1] 排序后,两个元素在内存中不会交换位置)
Chunk:会传入一个数字,对slice按照数字进行切割,并返回若干迭代器。比如一个slice大小为10, 传入的数字为3, 调用Chunk函数会返回4个迭代器,其中前三个大小为3, 第四个大小为1,直接看例子会更加明显:pkg.go.dev/slices@mast…
【比较重要】标准库maps的改动
增加了几个函数,和slices包一样基本上也都是围绕迭代器的,命名基本也和slices包差不多,都是一些易用性的方法。例如
All 返回k,v 迭代器,Keys 返回k的迭代器,Values 返回v的迭代器。
【一般重要】新增标准库structs
这个包定义一些标记结构体,目前只有一个结构体HostLayout,如果结构体使用了HostLayout会改变结构体的内存布局(不知道具体的影响,没查到资料,有知道的吗?可以评论一下),用户自定义的结构体如果使用它有固定的格式, 下划线是变量名,如:
package main
import "structs"
type My struct {
_ structs.HostLayout
}
所以这个包我觉得对具体代码逻辑没有用,是用告诉速运行时、编译器改变一些结构体的底层逻辑,如内存布局等,优化性能可能会有帮助吧,所以未来我觉得还是有一定的意义。
【没什么用】新增unique标准库
提供可比较的数据类型的标准,目前该库只实现了一个Handle[T]的结构体,方法包括Make用于生成一个结构体,Value用于返回结构体持有的值。实际上这个库没有建设完,和我们直接比较两个值是一样的,使用这个库还要额外增加代码。只不过这个库在内存层面做了优化,如果调用Make方法创建Handle对象时已经存在相同的值创建的Handle对象,那么此时不会消耗多余的内存,而是直接引用之前创建好的对象。在类型比较时可以提升一点点性能,个人认为这个库目前没什么用处可以忽略它。使用方法如下:
package main
import "unique"
func main() {
h := unique.Make[string]("a")
check := unique.Make[string]("b")
fmt.Println(h==check)
}
【不重要】其他的标准库小改动(略)
都是增加一些易用性的方法,没有特别重大的改进。如:
生成uint为随机数的方法。
返回一个别名类型的原始的类型。
RuneLen方法如果传递的参数不是何方的utf-16则返回-1
等等
【不重要】工具链遥测,收集使用和崩溃数据帮助Go团队改进
Go1.23 开始Go工具链手机使用数据和崩溃数据帮助Go团队定位问题,改进代码。这项功能在本地命令打开或关闭,打开关闭方式:
// 安装命令
go install golang.org/x/telemetry/cmd/gotelemetry@latest
// 数据收集到本地,每周上传到
gotelemetry on
// 捕收剂数据,也不上传数据
gotelemetry off
更详细的使用说明:tip.golang.org/doc/telemet…
数据上传地址:telemetry.go.dev/
【一般】其他的变化
- go tool cgo 命令支持
-ldflags
参数
使用这个参数可以传递CGO_LDFLAGS参数,避免环境变量CGO_LDFLAGS过大的错误。这个是指导使用哪个C的库文件等配置。
- go.mod 增加 debug信息
在go.mod文件中增加的配置,配置debug的行为
godebug (
default=go1.21
panicnil=1
asynctimerchan=0
)
更详细的说明:tip.golang.org/doc/godebug
- go vet 静态分析支持 stdversion
- go trace
有优化,程序可恢复的场景更多
- 运行时优化
崩溃时打印的调用栈更有区分度,阅读更友好。goroutine崩溃后,打印的调用栈后,会折叠后续的goroutine的调用栈输出。
- 编译器的优化
Profile-guided optimization(PGO)优化,主要体现在编译时间上,使用PGO原来会增加一倍的编译时间,现在只增加少量的编译时间(小于10%)
空间使用也会减少,减少的幅度不大,而且主要是amd64和386平台。
PGO是Go1.21引进的新技术,用于编译优化的技术,利用profile数据对代码进行优化,提升性能。更多的关于PGO的详细资料:
- (一般重要)连接器的优化 (对linkname的限制)
在标准库和运行时代码中禁止使用 //go:linkname 。原有的还支持,新增的使用进行了限制。
关于linkname补充一下,在代码中使用linkname标签可以将方法指向另一个方法,可以导出保内使用的方法、变量。这种方式存在的问题是,被导出方是无感的。1.23做了一些限制:
- 在标准库中限制linkname的使用,只能使用handshake模式(被导出方也需要显示的增加linkanme标签),并且开启 -checklinkname=1标识才可以使用
- 目前没有限制非标准库的使用
可以看一下这个文档github.com/golang/go/i…
转载自:https://juejin.cn/post/7385776238788788233