Go 1.20 发行说明(翻译)

2023-10-12 15:39:29 浏览数 (1)

美国时间 2023 年 2 月 1 日,Go 团队官宣 Go 1.20 正式发布。下面让我们一起了解下 Go 1.20 为我们带来的新特性吧。

本文主要翻译自 Go 1.20 Release Notes - The Go Programming Language。

Go 1.20 简介

最新的 Go 版本 1.20 在 Go 1.19 发布六个月后发布。它的大部分变化在于工具链、运行时和库的实现。与往常一样,该版本保持了 Go 1 的兼容性承诺。我们期望几乎所有 Go 程序都能像以前一样继续编译和运行。

语言的变化

Go 1.20 对语言进行了四项更改。

Go 1.17 添加了从切片到数组指针的转换。Go 1.20 对此进行了扩展,允许从切片到数组的转换:给定一个切片 x,转为数组可以写成[4]byte(x),而不是先转成数组指针再解引用*(*[4]byte)(x)

unsafe 包定义了三个新函数 SliceData、String 和 StringData。与 Go 1.17 Slice 一起,这些函数现在提供了构造和解构切片和字符串值的完整能力,而不依赖于它们的确切表示。

规范现在定义结构体值一次比较一个字段,按照字段在结构体类型定义中出现的顺序进行比较,并在第一个不匹配处停止。先前可以将规范解读为好像除了第一个不匹配之外,所有字段都需要进行比较。类似地,规范现在定义数组值按索引递增的顺序一次比较一个元素。在这两种情况下,差异都会影响某些比较是否必须 panic。现有程序保持不变:新的规范措辞描述了实现始终执行的操作。

即使类型参数不严格可比较(可能在运行时比较出现恐慌),可比较类型(如普通接口)现在也可以满足约束。这使得用非严格可比类型实例化受 comparable 约束的类型参数(例如用户定义的泛型 map key 的类型参数)成为可能。非严格可比类型有接口类型或包含接口类型的复合类型等。

端口

Windows

Go 1.20 是可在 Windows 7、8、Server 2008 和 Server 2012 的任何版本上运行的最后一个版本。Go 1.21 至少需要 Windows 10 或 Server 2016。

Darwin and 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 command

该目录 $GOROOT/pkg 不再存储标准库的预编译包档案: go install 不再写入它们,go build 不再检查它们,Go 发行版也不再提供它们。相反,标准库中的包是根据需要构建的,并缓存在构建缓存中,就像位于 GOROOT 外部的包一样。此更改减少了 Go 发行版的大小,并且还避免了使用 cgo 的包的 C 工具链偏差。

go test -json 的实现已得到改进,使其更加健壮。运行的go test -json的程序不需要任何更新。直接调用go tool test2json的程序现在应该使用-v=test2json (例如go test -v=test2json./pkg.test -test.v=test2json)而不是仅仅-v选项。

关于 go test -json 的一个相关的更改是在每个测试程序执行开始时添加一个带有 Action 集的事件。当使用该命令运行多个测试时,这些启动事件保证按照与命令行上指定的包的相同顺序发出。

go 命令现在定义架构功能构建标签,例如 amd64.v2,以允许根据特定架构功能的存在或不存在来选择包实现文件。详情请参阅 go help buildconstraint。

go 子命令现在接受-C <dir>在执行命令之前将目录更改为<dir>,这对于需要在多个不同模块中执行命令的脚本可能很有用。

go build 和 go test 命令不再接受 -i 标志,该标志自 Go 1.16 起已被弃用。

gogenerate 命令现在接受-skip <pattern>来跳过 //go:generate 匹配<pattern>的指令。

go test 命令现在接受-skip <pattern>来跳过与<pattern>匹配的测试、子测试或示例。

当主模块位于 GOPATH/src, go install 不再将非 main 包的库安装到 GOPATH/pkg,并且 go list 不再报告此类软件包的 Target 字段。(在模块模式下,编译的包仅存储在构建缓存中 ,但一个错误导致 GOPATH 安装目标意外地保持有效。)

go build、go install 和其他与构建相关的命令现在支持 -pgo 标志,该标志可启用性能分析文件引导优化,这在下面的 Compiler 部分中有更详细的描述。-pgo 标志指定 profile 的文件路径。 指定 -pgo=auto 会使 go 命令在主包的目录中搜索名为 default.pgo 的文件,并使用它(如果存在)。此模式目前需要在命令行上指定单个 main 包,但我们计划在未来版本中取消此限制。 指定 -pgo=off 将关闭性能分析文件引导优化。

go build、go install 和其他与构建相关的命令现在支持 -cover 标志,该标志使用代码覆盖率检测来构建指定的目标。 下面的 Cover 部分对此进行了更详细的描述。

go version -m 命令现在支持读取更多类型的 Go 二进制文件,最值得注意的是,使用 go build -buildmode=c-shared 构建的 Windows DLL 和没有执行权限的 Linux 二进制文件。

Cgo

go 命令现在在没有 C 工具链的系统上默认禁用 cgo。更具体地说,当 CGO_ENABLED 环境变量未设置、CC 环境变量未设置且路径中未找到默认 C 编译器(通常为 clang 或 gcc)时,CGO_ENABLED 默认为 0。与往常一样,您可以通过显式设置 CGO_ENABLED 来覆盖默认值。

默认更改最重要的影响是,当 Go 安装在没有 C 编译器的系统上时,它现在将使用纯 Go 构建标准库中使用 cgo 的包,而不是使用预分发的包存档(已被删除)或尝试使用 cgo 并失败。这使得 Go 在一些最小的容器环境以及 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 上,如果使用 net 包的 Go 代码是使用 -buildmode=c-archive 构建的,则将生成的存档链接到 C 程序需要在链接 C 代码时传递 -lresolv。

在 macOS 上,竞争检测器已被重写,不使用 cgo:启用竞争检测器的程序可以在没有 Xcode 的情况下构建和运行。 在 Linux 和其他 Unix 系统以及 Windows 上,需要主机 C 工具链才能使用竞争检测器。

Cover

Go 1.20 支持收集程序(应用程序和集成测试)的代码覆盖率文件,而不仅仅是单元测试。

要收集程序的覆盖率数据,请使用 go build 的 -cover 标志构建它,然后运行生成的二进制文件,并将环境变量 GOCOVERDIR 设置为覆盖率文件的输出目录。 有关如何开始的更多信息,请参阅“集成测试覆盖率”登录页面。 有关设计和实现的详细信息,请参阅提案。

Vet

改进了嵌套函数对循环变量捕获的检测

vet 工具现在报告在子测试函数体内调用 T.Parallel() 后对循环变量的引用。 此类引用可能会从不同的迭代中观察到变量的值(通常会导致测试用例被跳过)或由于不同步的并发访问而导致的无效状态。

该工具还可以检测更多地方的引用错误。 以前它只会考虑循环体的最后一条语句,但现在它会递归地检查 if、switch 和 select 语句中的最后一条语句。

针对不正确时间格式的新诊断

vet 工具现在报告使用 Time.Format 和 time.Parse 的时间格式 2006-02-01 (yyyy-dd-mm)。 此格式不会出现在常见的日期标准中,但在尝试使用 ISO 8601 日期格式 (yyyy-mm-dd) 时经常被错误使用。

Runtime

垃圾收集器的一些内部数据结构经过重新组织,以提高空间效率和 CPU 效率。 此更改减少了内存开销,并将整体 CPU 性能提高了 2%。

垃圾收集器在 goroutine 协助方面的某些情况下,行为更加稳定。

Go 1.20 添加了一个新的 runtime/coverage 包,其中包含在运行时写覆盖率分析数据的 API。

Compiler

Go 1.20 添加了基于性能分析的优化(profile-guided optimization,PGO) 的预览支持。 PGO 使工具链能够根据运行时性能分析信息执行特定于应用程序和工作负载的优化。目前,编译器支持 pprof CPU 性能分析文件,可以通过常规方式收集这些配置文件,例如 runtime/pprof 或 net/http/pprof 包。要启用 PGO,请通过 -pgo 标志传递 pprof 文件的路径以进行构建,正如前文所说的那样。Go 1.20 使用 PGO 在热调用站点会更积极地使用内联函数。一组代表性 Go 程序的基准测试显示,启用基于性能分析的内联优化可将性能提高约 3-4%。有关详细文档,请参阅 PGO 用户指南。我们计划在未来的版本中添加更多基于性能分析的优化。请注意,PGO 是预览版,因此请谨慎使用。

Go 1.20 编译器升级了其前端,以使用一种处理编译器内部数据的新方法,该方法修复了多个泛型类型问题并支持泛型函数和方法中的类型声明。

现在,编译器默认拒绝匿名接口循环并出现编译器错误。这些源于嵌入接口的使用,并且始终存在微妙的正确性问题,但我们没有证据表明它们在实践中实际使用。假设没有用户报告受到此更改的不利影响,我们计划更新 Go 1.22 的语言规范以正式禁止它们,以便工具作者也可以停止支持它们。

Go 1.18 和 1.19 的构建速度有所下降,这主要是由于增加了对泛型和后续工作的支持。Go 1.20 将构建速度提高了 10%,与 Go 1.17 保持一致。相对于 Go 1.19,生成的代码性能也普遍略有提高。

Linker

在 Linux 上,链接器现在在链接时选择 glibc 或 musl 的动态解释器。

在 Windows 上,Go 链接器现在支持基于 LLVM 的现代 C 工具链。

Go 1.20 使用 go: 和 type: 前缀来表示编译器生成的符号,而不是 go. 和 type.。这可以避免名称以 go. 开始的用户包。debug/gosym 包支持使用 Go 1.20 及更高版本构建的二进制文件的新命名约定。

Bootstrap

从源代码构建 Go 版本且未设置 GOROOT_BOOTSTRAP 时,以前版本的 Go 在 HOME/go1.4 目录(Windows 上为%HOMEDRIVE%%HOMEPATH%go1.4)中查找 Go 1.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 1.22 将需要 Go 1.20 的最终版本来进行引导。

Core library

新的加密包 crypto/ecdh

Go 1.20 添加了一个新 crypto/ecdh 包,为 NIST 曲线和 Curve25519 上的椭圆曲线 Diffie-Hellman 密钥交换提供显式支持。

程序应使用 crypto/ecdh 而不是较低级别功能 crypto/elliptic 以实现 ECDH,并使用第三方模块来实现更高级的用例。

包装多个错误

Go 1.20 扩展了对错误包装的支持,以允许一个错误包装多个其他错误。

通过提供返回 []error 的 Unwrap 方法,错误 e 可以包装多个错误。

error.Is 和 error.As 函数已更新以检查多重包装的错误。

fmt.Errorf 函数现在支持多次出现 %w 格式动词,这将导致它返回包含一个包含多个错误的错误。

新函数 errors.Join 返回一个包含错误列表的错误。

HTTP 响应控制器

新的“net/http”.ResponseController 类型提供对“net/http”.ResponseWriter 接口未处理的扩展每个请求功能的访问。

之前,我们通过定义 ResponseWriter 可以实现的可选接口(例如 Flusher)添加了新的按请求功能。 这些接口不可发现且使用起来很笨拙。

ResponseController 类型提供了一种更清晰、更容易发现的方式来添加每个处理程序控件。 Go 1.20 中还添加了两个这样的控件:SetReadDeadline 和 SetWriteDeadline,它们允许设置每个请求的读取和写入截止时间。 例如:

代码语言:javascript复制
func RequestHandler(w ResponseWriter, r *Request) {
  rc := http.NewResponseController(w)
  rc.SetWriteDeadline(time.Time{}) // disable Server.WriteTimeout when sending a large response
  io.Copy(w, bigData)
}

新的 ReverseProxy 重写钩子

转发代理 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时,默认情况下不会添加这些标头。

使用这些功能的重写挂钩的示例是:

代码语言:javascript复制
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 的兼容性承诺。 还有各种性能改进,这里就不一一列举了。

archive/tar

当设置 GODEBUG=tarinsecurepath=0 环境变量时,Reader.Next 方法现在将为文件名为绝对路径、引用当前目录之外的位置、包含无效字符或 ( 在 Windows 上)是保留名称,例如 NUL。 Go 的未来版本可能会默认禁用不安全路径。

archive/zip

设置 GODEBUG=zipinsecurepath=0 环境变量后,NewReader 现在将在打开包含绝对路径、引用当前目录之外的位置、包含无效字符或(在 Windows)是保留名称,例如 NUL。 Go 的未来版本可能会默认禁用不安全路径。

从包含文件数据的目录文件中读取现在将返回错误。 zip 规范不允许目录文件包含文件数据,因此此更改仅影响从无效存档中读取。

bytes

新的 CutPrefix 和 CutSuffix 函数类似于 TrimPrefix 和 TrimSuffix,但也报告字符串是否被修剪。

新的 Clone 函数分配字节切片的副本。

context

新的 WithCancelCause 函数提供了一种取消具有给定错误的上下文的方法。 可以通过调用新的 Cause 函数来检索该错误。

crypto/ecdsa

当使用支持的曲线时,所有操作现在都在恒定时间内实现。 这导致 CPU 时间增加 5% 到 30%,主要影响 P-384 和 P-521。

新的 PrivateKey.ECDH 方法将 ecdsa.PrivateKey 转换为 ecdh.PrivateKey。

crypto/ed25519

PrivateKey.Sign 方法和VerifyWithOptions 函数现在支持使用Ed25519ph 对预哈希消息进行签名,由返回crypto.SHA512 的Options.HashFunc 指示。 它们现在还支持带上下文的 Ed25519ctx 和 Ed25519ph,通过设置新的 Options.Context 字段来指示。

crypto/rsa

新字段OAEPOptions.MGFHash 允许单独配置 MGF1 哈希以进行 OAEP 解密。

crypto/rsa 现在使用新的、更安全的、恒定时间的后端。这会导致解密操作的 CPU 运行时间增加约 15%(amd64 上的 RSA-2048)和 45%(arm64 上的 RSA-4096),在 32 位架构上增加更多。加密操作比以前慢大约 20 倍(但仍比解密快 5-10 倍)。预计性能将在未来版本中得到改进。程序不得修改或手动生成 PrecomputedValues.

crypto/subtle

新函数XORBytes 将两个字节片异或在一起。

crypto/tls

解析的证书现在在所有主动使用该证书的客户端之间共享。 对于与共享其证书链的任何部分的服务器或服务器集合建立许多并发连接的程序,内存节省可能非常重要。

对于由于证书验证失败而导致的握手失败,TLS 客户端和服务器现在返回新的类型 CertificateVerificationError 错误,其中包括所提供的证书。

crypto/x509

ParsePKCS8PrivateKey 和 MarshalPKCS8PrivateKey 现在支持 *crypto/ecdh.PrivateKey 类型的密钥。 ParsePKIXPublicKey 和 MarshalPKIXPublicKey 现在支持 *crypto/ecdh.PublicKey 类型的密钥。 解析 NIST 曲线键仍返回 *ecdsa.PublicKey 和 *ecdsa.PrivateKey 类型的值。 使用他们的新 ECDH 方法转换为加密/ECDH 类型。

新的 SetFallbackRoots 函数允许程序定义一组后备根证书,以防操作系统验证程序或标准平台根包在运行时不可用。 它最常与新包 golang.org/x/crypto/x509roots/fallback 一起使用,它将提供最新的根包。

debug/elf

尝试使用 Section.Data 或 Section.Open 返回的读取器读取 SHT_NOBITS 节现在会返回错误。

定义了附加的 R_LARCH_* 常量以供 LoongArch 系统使用。

定义了附加的 R_PPC64_* 常量以与 PPC64 ELFv2 重定位一起使用。

R_PPC64_SECTOFF_LO_DS 的常量值已更正,从 61 更改为 62。

debug/gosym

由于 Go 符号命名约定的更改,处理 Go 二进制文件的工具应使用 Go 1.20 的 debug/gosym 包来透明地处理旧的和新的二进制文件。

debug/pe

定义了其他 IMAGE_FILE_MACHINE_RISCV* 常量以供 RISC-V 系统使用。

encoding/binary

ReadVarint 和 ReadUvarint 函数现在将在读取部分值后返回 io.ErrUnexpectedEOF,而不是 io.EOF。

encoding/xml

新的 Encoder.Close 方法可用于在完成编码后检查未关闭的元素。

解码器现在拒绝带有多个冒号的元素和属性名称(例如 <a

0 人点赞