草案发布说明-介绍Go 1.20
Go 1.20尚未发布。这些是正在进行中的发布说明。Go 1.20预计将于2023年2月发布。
语言的改变
Go 1.20对语言进行了四项更改。
Go 1.17增加了从切片到数组指针的转换。Go 1.20扩展了这一点,允许从slice到数组的转换:给定一个slice x,现在可以写入[4]byte(x)
而不是*(*[4]byte)(x)
。
这个unsafe包定义了三个新函数SliceData
、String
和StringData
。随着Go 1.17的Slice
,这些函数现在提供了构造和解构Slice
和字符串值的完整功能,而不依赖于它们的确切表示。
规范现在定义,struct values一次比较一个字段,按照它们在结构类型定义中出现的顺序考虑字段,并在第一次不匹配时停止。该规范以前可以被解读为,除了第一个不匹配之外,所有字段都需要进行比较。类似地,规范现在定义了数组值每次比较一个元素,按索引顺序递增。在这两种情况下,这种差异会影响某些比较是否必须panic。现有的程序没有改变:新的规范措辞描述了实现一直在做的事情。
可比较类型(例如普通interface)现在可以满足可比较约束,即使类型参数不是严格可比较的(比较可能在运行时出现panic)。这使得用非严格可比类型参数(如接口类型或包含接口类型的复合类型)实例化由可比类型约束的类型参数(例如,用户定义的泛型映射键的类型参数)成为可能。
端口
Darwin 和 iOS
Go 1.20是运行在macOS 10.13 High Sierra或10.14 Mojave上的最后一个版本。Go 1.21需要macOS 10.15 Catalina或更高版本。
FreeBSD/RISC-V
Go 1.20在RISC-V上增加了对FreeBSD的实验性支持(GOOS=FreeBSD, GOARCH=riscv64)。
工具箱
Go命令
目录$GOROOT/pkg
不再为标准库存储预编译的包存档:go install
不再write它们,go build
不再检查它们,go发行版不再发布它们。相反,标准库中的包是根据需要构建的,并缓存在构建缓存中,就像GOROOT外部的包一样。这一变化减少了Go发行版的大小,也避免了使用cgo的包的C工具链倾斜。
go test -json
的实现经过了改进,使其更加健壮。运行go test -json
的程序不需要任何更新。直接调用go工具test2json的程序现在应该使用-v=json运行测试二进制文件(例如,go test -v=json
或 ./pkg.test -test.v=json
)而不是普通的-v。
go test -json
的一个相关更改是添加了一个事件,Action设置为在每个测试程序执行开始时启动。当使用go命令运行多个测试时,这些启动事件保证以与命令行上命名的包相同的顺序发出。
go命令现在定义了架构特性构建标记,比如amd64。V2,允许基于特定体系结构特性的存在或不存在来选择包实现文件。详情请参见go help buildconstraint
。
go子命令现在接受-C <dir>
来在执行命令之前将directory更改为<dir>
,这对于需要在多个不同模块中执行命令的脚本可能很有用。
go构建和go测试命令不再接受-i标志,该标志自go1.16以来已弃用。
go generate
命令现在接受-skip <pattern>
来跳过 //go:generate
匹配<pattern>
的指令。
go test
命令现在接受-skip <pattern>
来跳过匹配<pattern>
的测试、子测试或示例。
当主模块位于GOPATH/src
内时,go install
不再将非主包的库安装到GOPATH/pkg
, go list
不再报告此类包的Target
字段。(在模块模式下,编译后的包只存储在构建缓存中,但是一个Bug导致GOPATH安装目标意外地保持有效。)
go build
、go install
和其他与构建相关的命令现在支持-pgo
标志,以启用概要文件引导的优化,下面的编译器部分将对此进行更详细的描述。-pgo
标志指定配置文件的文件路径。指定-pgo=auto
将导致go命令搜索名为default的文件。在主包的目录中使用Pgo,如果存在就使用它。这种模式目前需要在命令行上指定一个主包,但我们计划在未来的版本中取消这一限制。指定-pgo=off
关闭配置文件引导的优化。
go build
、go install
和其他与构建相关的命令现在支持一个-cover
标志,该标志使用代码覆盖工具构建指定的目标。下面的cover部分将对此进行更详细的描述。
go version
go version -m
命令现在支持读取更多类型的go二进制文件,最值得注意的是,使用go build -buildmode=c-shared
构建的Windows dll和没有执行权限的Linux二进制文件。
Cgo
在没有C工具链的系统上,go命令现在默认禁用cgo。更具体地说,当CGO_ENABLED环境变量未设置,CC环境变量未设置,并且在路径中没有找到默认的C编译器(通常是clang或gcc)时,CGO_ENABLED默认为0。与往常一样,你可以通过显式设置CGO_ENABLED来覆盖默认值。
默认更改最重要的影响是,当Go安装在没有C编译器的系统上时,它现在将对使用cgo的标准库中的包使用纯Go构建,而不是使用预分布式包存档(如上所述,已被删除)或尝试使用cgo而失败。这使得Go在一些最小容器环境和macOS上工作得更好,在macOS上,自Go 1.16以来,预分布式包存档就没有用于基于cgo的包。
标准库中使用cgo的包有net
、os/user
和plugin
。在macOS上,net
和os/user
包已经被重写为不使用cgo
:现在用于cgo和非cgo构建以及交叉编译构建的代码相同。在Windows上,net
和os/user
包从未使用过cgo。在其他系统上,禁用cgo的构建将使用这些包的纯Go版本。
在macOS上,已经重写了 race detector,不再使用cgo:race-detector-enabled程序可以在没有Xcode的情况下构建和运行。在Linux和其他Unix系统以及Windows上,需要一个主机C工具链来使用竞争检测器。
Cover
Go 1.20支持收集程序(应用程序和集成测试)的代码覆盖概要文件,而不仅仅是单元测试。
要为程序收集覆盖率数据,请使用go build的-cover标志构建它,然后运行生成的二进制文件,并将环境变量GOCOVERDIR设置为覆盖率概要文件的输出目录。有关如何开始的更多信息,请参阅“集成测试的覆盖率”登录页。详细的设计和实现请参见方案。
Vet
改进了通过嵌套函数捕获循环变量的检测
vet工具现在在子测试函数体中调用T.Parallel()
后报告对循环变量的引用。这样的引用可能会观察到来自不同迭代的变量值(通常会导致跳过测试用例),或者由于不同步的并发访问而导致的无效状态。
该工具还可以在更多的地方检测引用错误。以前它只考虑循环体的最后一条语句,但现在它递归地检查if、switch和select语句中的最后一条语句。
新的诊断错误的时间格式
vet工具现在报告使用时间格式2006-02-01 (yyyy-dd-mm)。格式和时间。这种格式没有出现在常见的日期标准中,但在尝试使用ISO 8601日期格式(yyyy-mm-dd)时经常被错误地使用。
Runtime
Runtime现在有了对memory-safe arena分配的实验性支持,这使得大量急切地释放内存成为可能。如果使用得当,在内存分配较多的应用程序中,它有可能将CPU性能提高15%。要尝试一下,使用GOEXPERIMENT=arenas构建Go程序,这将使arena包对程序可见。导入arena包的源文件必须要求goexperiment.arenas
构建标签。
一些垃圾收集器的内部数据结构被重新组织,以提高空间和CPU效率。这一更改减少了内存开销,并将整体CPU性能提高了2%。
在某些情况下,垃圾收集器相对于goroutine辅助程序的行为不那么不稳定。
Go 1.20增加了一个新的runtime/coverage ,其中包含api,用于在运行时从不通过os.Exit()终止的long-running and/or 服务器程序写入覆盖配置文件数据。
编译器
Go 1.20增加了对配置文件引导优化(PGO)的预览支持。PGO使工具链能够基于运行时概要信息执行特定于应用程序和工作负载的优化。目前,编译器支持pprof CPU配置文件,可以通过通常的方式收集,例如runtime/pprof或net/http/pprof包。要启用PGO,可以通过-pgo标志传递pprof配置文件的路径以进行构建,如上所述。Go 1.20在hot call sites更积极地使用PGO内联函数。一组具有代表性的Go程序的基准测试显示,启用profile-guided 的内联优化可以提高约3-4%的性能。我们计划在未来的版本中添加更多的配置文件引导优化。注意,配置文件引导的优化是一个预览,所以请谨慎使用它。
Go 1.20编译器升级了前端,以使用一种新的方式处理编译器的内部数据,这修复了几个泛型类型的错误,并在泛型函数和方法中启用了局部类型。
编译器现在不允许匿名接口循环。
相对于Go 1.19,生成的代码性能总体上略有提高,build wall 的时间略有增加, build user的时间略有减少。
Linker
在Linux上,Linker现在在链接时为glibc或musl选择动态解释器。
在Windows上,Go Linker现在支持现代的基于llvm的C工具链。
Go 1.20对编译器生成的符号使用 go:
和 type:
前缀,而不是 go.
and type.
。这避免了名称以go.
开头的用户包的混淆。debug/gosym
包理解使用Go 1.20或更新版本构建的二进制文件的新命名约定。
Bootstrap
当从源代码构建一个Go版本并且没有设置GOROOT_BOOTSTRAP时,以前版本的Go会在$HOME/go1.4目录下查找Go 1.4或更高版本的引导工具链(Windows上的%HOMEDRIVE%%HOMEPATH%\go1.4
)。Go 1.18和Go 1.19首先寻找$HOME/go1.17
或$HOME/sdk/go1.17
,然后回到$HOME/go1.4
,在引导Go 1.20时需要使用Go 1.17。Go 1.20确实需要一个Go 1.17版本来进行引导,但是我们意识到我们应该采用引导工具链的最新点版本,所以它需要Go 1.17.13
。Go 1.20在回到$HOME/go1.4
之前寻找$HOME/go1.17.13
或$HOME/sdk/go1.17.13
(以支持硬编码路径$HOME/go1.4但在那里安装了更新的Go工具链的系统)。在未来,我们计划大约每年将引导工具链向前移动一次,特别是我们预计Go 1.22将需要Go 1.20的最终点发布来进行引导。
核心库
新的crypto/ecdh包
Go 1.20增加了一个新的crypto/ecdh包,为NIST曲线和Curve25519上的椭圆曲线Diffie-Hellman密钥交换提供直接支持。
程序应该倾向于使用crypto/ecdh或crypto/ecdsa,而不是crypto/elliptic中的低级功能。
包装多个错误
Go 1.20扩展了对错误包装的支持,允许一个错误包装多个其他错误。
错误e
可以通过提供返回[]error
的Unwrap
方法来包装多个错误。
errors.Is
和errors.As
函数已更新,以检查多重包装错误。
fmt.Errorf
函数现在支持%w
格式动词的多次出现,这将导致它返回一个包含所有这些错误操作数的错误。
新函数errors.Join
返回一个错误,其中包含一个错误列表。
HTTP ResponseController
新的“net/http”。ResponseController类型提供了对“net/http”无法处理的每个请求的扩展功能的访问。ResponseWriter接口。
以前,我们通过定义ResponseWriter可以实现的可选接口(如Flusher)添加了新的逐请求功能。这些接口无法被发现,使用起来也很笨拙。
ResponseController类型提供了一种更清晰、更易发现的方式来添加每个处理程序控件。Go 1.20中还添加了两个这样的控件SetReadDeadline和SetWriteDeadline,它们允许设置每个请求的读写截止日期。例如:
func RequestHandler(w ResponseWriter, r *Request) {
rc := http.NewResponseController(w)
rc.SetWriteDeadline(0) // disable Server.WriteTimeout when sending a large response
io.Copy(w, bigData)
}
新的ReverseProxy Rewrite钩子
httputil.ReverseProxy
转发代理包含了一个新的Rewrite钩子函数,取代了之前的Director钩子。
Rewrite钩子接受一个ProxyRequest参数,该参数包括代理接收到的入站请求和它将发送的出站请求。与仅对出站请求操作的Director钩子不同,这允许Rewrite钩子避免某些情况,即恶意入站请求可能导致钩子添加的头文件在转发前被删除。参见issue #50580。
ProxyRequest.SetURL
方法将出站请求路由到提供的目的地,并取代NewSingleHostReverseProxy
函数。与NewSingleHostReverseProxy
不同,SetURL还设置出站请求的Host报头。
ProxyRequest.SetXForwarded
方法设置出站请求的X-Forwarded-For、X-Forwarded-Host和X-Forwarded-Proto头。在使用重写时,默认情况下不会添加这些标头。
一个使用这些特性的Rewrite钩子的例子是:
proxyHandler := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
r.SetURL(outboundURL) // Forward request to outboundURL.
r.SetXForwarded() // Set X-Forwarded-* headers.
r.Out.Header.Set("X-Additional-Header", "header set by the proxy")
},
}
当传入的请求没有User-Agent头时,ReverseProxy不再向转发的请求添加User-Agent头。
对库的一些小更改
与往常一样,库中有各种微小的变化和更新,这些变化和更新是考虑到Go 1的兼容性承诺而做出的。还有各种性能改进,这里没有列举。
译者注:由于涉及到的包比较多,限于篇幅,本文不做进一步讲解。
评论(0)