本文首发自「慕课网」,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"!
作者:tonybai | 慕课网讲师
Go 1.21版本正在热火朝天地开发当中,依照Go外围团队的一年两次的公布节奏来算,Go 1.21版本预计将在2023年8月公布(Go 1.20版本是在2023年2月份公布的)。
本文将和大家一起看看Go 1.21都会带来哪些新个性。不过因为目前为时尚早,上面列出的有些变动最终不肯定能进入到Go 1.21的最终版本中,所以切记所有变更要以最终Go 1.21版本公布时为准。
在细数变动之前,咱们先来看看Go语言的以后状态。
1. Go语言以后状态
Go语言的2022年初排名为12位,同时TIOBE官网编辑也提到:“在新兴编程语言中,Go是惟一一个可能在将来冲入前十的后端编程语言”。Go语言的倒退仿佛应验了这一预测,在往年的3月份,Go就再次进入编程语言排行榜前十:
一个月后的四月初,TIOBE排行榜上,Go稳住了第10名的位次:
在国内,在鹅厂前不久公布的《2022年腾讯研发大数据报告》中,
在国内,继Go在2021年从C++手中躲过红旗首次登顶鹅厂最热门编程语言之后,在鹅厂前不久公布的《2022年腾讯研发大数据报告》中,Go蝉联鹅厂最热门编程语言,持续夯实在国内头部互联网公司内的劣势位置:
Go于2009年开源,在经验多年的宣传和宣扬后,Go目前进入了安稳倒退的阶段。疫情完结后,原先线上举办或勾销的国内外的Go技术大会当初陆续又都开始复原了,置信这会让更多开发人员接触到Go。像Go这样的能在世界各地继续多年举办技术大会的语言真是不多了。
接下来,咱们就来聚焦到Go 1.21版本,开掘一下这个版本都有哪些新个性。
2. 语言变动
目前Go 1.21版本里程碑中波及语言变动的有大概2项,咱们来看看。
2.1 减少clear预约义函数
Go 1.21减少了一个clear预约义函数用来做切片和map的clear操作,其原型如下:
// $GOROOT/src/builtin.go// The clear built-in function clears maps and slices.// For maps, clear deletes all entries, resulting in an empty map.// For slices, clear sets all elements up to the length of the slice// to the zero value of the respective element type. If the argument// type is a type parameter, the type parameter's type set must// contain only map or slice types, and clear performs the operation// implied by the type argument.func clear[T ~[]Type | ~map[Type]Type1](t T)
clear是针对map和slice的操作函数,它的语义是什么呢?咱们通过一个例子来看一下:
package mainimport "fmt"func main() { var sl = []int{1, 2, 3, 4, 5, 6} fmt.Printf("before clear, sl=%v, len(sl)=%d, cap(sl)=%d\n", sl, len(sl), cap(sl)) clear(sl) fmt.Printf("after clear, sl=%v, len(sl)=%d, cap(sl)=%d\n", sl, len(sl), cap(sl)) var m = map[string]int{ "tony": 13, "tom": 14, "amy": 15, } fmt.Printf("before clear, m=%v, len(m)=%d\n", m, len(m)) clear(m) fmt.Printf("after clear, m=%v, len(m)=%d\n", m, len(m))}
运行该程序:
before clear, sl=[1 2 3 4 5 6], len(sl)=6, cap(sl)=6after clear, sl=[0 0 0 0 0 0], len(sl)=6, cap(sl)=6before clear, m=map[amy:15 tom:14 tony:13], len(m)=3after clear, m=map[], len(m)=0
咱们看到:
- 针对slice,clear放弃slice的长度和容量,但将所有slice内已存在的元素(len个)都置为元素类型的零值;
- 针对map,clear则是清空所有map的键值对,clear后,咱们将失去一个empty map。
2.2 扭转panic(nil)语义
应用defer+recover捕捉panic是Go语言惟一解决panic的办法,其典型模式如下:
package mainimport "fmt"func foo() { defer func() { if err := recover(); err != nil { fmt.Printf("panicked: %v\n", err) return } fmt.Println("it's ok") }() panic("some error")}func main() { foo()}
运行下面程序会输入:panicked: some error
例子中咱们向panic传入了示意panic起因的字符串,panic的参数是一个interface{}类型,能够传入任意值,当然也能够传入nil。
比方下面例子,当咱们给foo函数的panic调用传入nil时,咱们将失去上面后果:it's ok
这可能会给开发者带去纳闷:明明是触发了panic,但函数却依照失常逻辑解决!2018年,前Go外围团队成员bradfitz就提出了一个issue:spec: guarantee non-nil return value from recover,提出当开发者调用panic(nil)时,recover应该返回某种runtime error,而不是nil。这个issue在往年被纳入了Go 1.21版本,当初该issue的实现曾经被merge到了骨干。
新的实现在src/runtime/panic.go中定义了一个名为PanicNilError的新Error:
// $GOROOT/src/runtime/panic.go// A PanicNilError happens when code calls panic(nil).//// Before Go 1.21, programs that called panic(nil) observed recover returning nil.// Starting in Go 1.21, programs that call panic(nil) observe recover returning a *PanicNilError.// Programs can change back to the old behavior by setting GODEBUG=panicnil=1.type PanicNilError struct { // This field makes PanicNilError structurally different from // any other struct in this package, and the _ makes it different // from any struct in other packages too. // This avoids any accidental conversions being possible // between this struct and some other struct sharing the same fields, // like happened in go.dev/issue/56603. _ [0]*PanicNilError}func (*PanicNilError) Error() string { return "panic called with nil argument" }func (*PanicNilError) RuntimeError() {}
Go编译器会将panic(nil)替换为panic(new(runtime.PanicNilError)),这样咱们用Go 1.21版本运行下面的程序,咱们就会失去上面后果了:
panicked: panic called with nil argument
如果你的遗留代码中调用了panic(nil)(注:显然这不是一种很idiomatic的作法),降级到Go 1.21版本后你就要小心了。如果你想保留原先的panic(nil)行为,能够用GODEBUG=panicnil=1。
有童鞋可能会质疑这违反了Go1兼容性承诺,但实际上Go1兼容性标准保留了对语言标准中不统一或谬误的订正势力,即使这种订正会导致遗留代码呈现与原先不统一的行为。
3. 编译器与工具链
每个Go版本中,编译器和工具链的改变都不少,咱们挑重点看一下:
3.1 一些OS的最小反对版本的更新
Go 1.21开始,go installer反对最小macOS版本更新为10.15,而最小Windows版本为Windows 10。
3.2 低版本的go编译器将回绝编译高版本的go module
从Go 1.21版本开始,低版本的go编译器将回绝编译高版本的go module(go.mod中go version标识最低版本) ,这也是Russ Cox策动的Go扩大的向前兼容性提案的一部分。此外,Go扩大向前兼容性提案感觉比较复杂,可能不会全副在Go 1.21版本落地。
3.3 反对WASI
Go从1.11版本就开始反对将Go源码编译为wasm二进制文件,并在反对wasm的浏览器环境中运行。
不过WebAssembly绝不仅仅被设计为仅限于在Web浏览器中运行,外围的WebAssembly语言是独立于其周围环境的,WebAssembly齐全能够通过API与内部世界互动。在Web上,它天然应用浏览器提供的现有Web API。然而,在浏览器之外,之前还没有一套规范的API能够让WebAssembly程序应用。这使得创立真正可移植的非Web WebAssembly程序变得艰难。
WebAssembly System Interface(WASI)是一个填补这一空白的倡导,它有一套洁净的API,能够由多个引擎在多个平台上实现,并且不依赖于浏览器的性能(只管它们依然能够在浏览器中运行)。
Go 1.21将减少对WASI的反对,初期先反对WASI Preview1版本,之后会反对WASI Preview2版本,直至最终WASI API版本公布!目前咱们能够应用GOOS=wasip1 GOARCH=wasm将Go源码编译为反对WASI的wasm程序,上面是一个例子:
// main.gopackage main func main() { println("hello") }
下载最新go dev版本后(go install http://golang.org/dl/gotip@latest),能够执行上面命令将main.go编译为wasm程序:
$ GOARCH=wasm GOOS=wasip1 gotip build -o main.wasm main.go
开源的wasm运行时有很多,wazero是目前比拟火的且应用纯Go实现的wasm运行时程序,装置wazero后,能够用来执行下面编译进去的main.wasm:
$curl https://wazero.io/install.sh$wazero run main.wasmhello
3.4 Go 1.21可能推出纯动态工具链,不再依赖glibc
应用纯Go实现的net resolver,原先DNS的问题也将被解决,这样Go团队在构建工具链的时候应用CGO_ENABLED=0构建出动态工具链,没有动态链接库的依赖。
3.5 go test -c反对为多个包同时构建测试二进制程序
Go 1.21版本之前,go test -c仅反对将单个包的测试代码编译为测试二进制程序,Go 1.21版本则容许咱们同时为多个包构建测试二进制程序。
上面是官网给出的例子:
$ go test -c -o /tmp ./pkg1 ./pkg2 ./pkg2$ ls /tmppkg1.test pkg2.test pkg3.test
3.6 减少$GOROOT/go.env
明天应用go env
-w命令批改的默认环境变量会写入:filepath.Join(os.UserConfigDir(),
“go/env”)。在Mac上,这个门路是*$HOME/Library/Application
Support/go/env;在Linux上,这个门路是$HOME/.config/go/env*。
Go 1.21将减少一个全局档次上的go.env,放在$GOROOT上面,目前默认的go.env为:
// $GOROOT/go.env# This file contains the initial defaults for go command configuration.# Values set by 'go env -w' and written to the user's go/env file override these.# The environment overrides everything else.# Use the Go module mirror and checksum database by default.# See https://proxy.golang.org for details.GOPROXY=https://proxy.golang.org,directGOSUMDB=sum.golang.org
咱们依然能够通过go env-w命令批改user级的env文件来笼罩上述配置,当然最高优先级的是OS用户环境变量,如果在OS用户环境变量文件(比方.bash_profile、.bashrc)中设置了Go的环境变量值,比方GOPROXY等,那么以OS用户环境变量为优先。
4. 规范库
咱们接下来再来看看变更最多的一部分:规范库,咱们将对次要变更项作简要介绍。
4.1 slices和maps进入规范库
Go 1.18版本泛型落地公布前的最初一刻,Rob Pike叫停了slices、maps等泛型包的入库,起初这两个包先搁置在golang.org/x/exp下作为试验包。随着Go泛型日益成熟以及Go团队对泛型应用教训的增多,Go团队终于决定将golang.org/x/exp/slices和golang.org/x/exp/maps在Go 1.21版本中将挪入规范库。
4.2 log/slog退出规范库
log/slog是Go官方版结构化日志包,大抵与uber的zap包相当。在我之前的一篇文章《slog:Go官方版结构化日志包》有对slog的详尽阐明,大家能够移步到那篇文章看看。不过slog的proposal仍旧很多,后续slog可能会有继续改良和变更,与那篇文章中的内容可能会有一些差别。
4.3 sync包减少OnceFunc、OnceValue和OnceValues
在sync.Once的根底上,这个issue减少了三个与Once相干的"语法糖"API,用在一些对Once有需要的最常见的场景中。
4.4 减少testing.Testing函数
Go 1.21为testing包减少了func Testing() bool函数,该函数能够用来报告以后程序是否是go test创立的测试程序。应用Testing函数,咱们能够确保一些无需在单测阶段执行的函数不被执行。比方上面例子来自这个issue:
// file/that/should/not/be/used/from/testing.gofunc prodEnvironmentData() *Environment { if testing.Testing() { log.Fatal("Using production data in unit tests") } ....}
4.5 一些变更点
- context: 减少为deadline或timeout context设置cancel起因的API - https://github.com/golang/go/issues/56661
- unicode降级到15.0版本 - <https://github.com/golang/go/issues/55079
5. 参考资料
- Go 1.21 milestone - https://github.com/golang/go/milestone/279
欢送关注「慕课网」帐号,咱们会始终保持内容原创,提供IT圈优质内容,分享干货常识,大家一起独特成长吧!
本文原创公布于慕课网 ,转载请注明出处,谢谢合作