Go 1.20 版本说明

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 新增了三个函数: SliceDataStringStringData。 结合 Go 1.17 的 Slice 函数,这些功能现已能完整地 构造与解构切片和字符串值,且无需依赖其内部具体表示。

规范现已明确定义:结构体值的比较将逐字段进行, 按结构体类型定义中字段出现的顺序处理, 并在首个不匹配字段处停止比较。 此前规范可能被理解为:即使发现首个不匹配后, 仍需比较所有后续字段。 类似地,规范现在定义数组值的比较 按索引递增顺序逐个元素进行。 上述两处差异会影响特定比较是否必须触发 panic。 现有程序不受影响:新规范的措辞描述了 现有实现一贯的实际行为。

可比较类型(如普通接口) 现在可满足 comparable 约束,即使类型实参 并非严格可比较(运行时比较可能 panic)。 这使得能够用非严格可比较的类型实参 (例如接口类型,或包含接口类型的复合类型) 实例化受 comparable 约束的类型参数 (例如用户定义泛型映射键的类型参数)。

平台支持

Windows

Go 1.20 是支持 Windows 7、8、Server 2008 和 Server 2012 任意发行版的最后一个版本。 Go 1.21 将要求至少 Windows 10 或 Server 2016。

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=freebsdGOARCH=riscv64)。

工具

Go 命令

目录 $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 字段为 start 的事件。 使用 go 命令运行多个测试时, 这些开始事件的发出顺序将保证 与命令行中指定的包顺序一致。

go 命令现已定义 架构特性构建标签(如 amd64.v2), 以便根据特定架构特性是否存在 来选择包实现文件。 详情请参阅 go help buildconstraint

go 子命令现接受 -C <dir> 参数,可在执行命令前将工作目录切换至 <dir>, 这对需要在多个不同模块中执行命令的脚本可能很有用。

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

go generate 命令现接受 -skip <pattern> 参数,以跳过 与 <pattern> 匹配的 //go:generate 指令。

go test 命令现接受 -skip <pattern> 参数,以跳过 与 <pattern> 匹配的测试、子测试或示例。 当主模块位于 GOPATH/src 内时,go install 不再将非 main 包的库安装到 GOPATH/pkg,并且 go list 不再为此类包报告 Target 字段。(在模块模式下,编译后的包仅存储在构建缓存中,但一个缺陷曾导致 GOPATH 安装目标意外生效。)

go buildgo install 及其他构建相关命令现在支持 -pgo 标志,以启用配置文件引导优化,该优化在下面的编译器部分有更详细的描述。-pgo 标志指定配置文件的文件路径。指定 -pgo=auto 会使得 go 命令在主包目录中搜索名为 default.pgo 的文件,并在找到时使用它。此模式当前要求在命令行中指定单个主包,但我们计划在未来的版本中取消此限制。指定 -pgo=off 可关闭配置文件引导优化。

go buildgo install 及其他构建相关命令现在支持 -cover 标志,该标志构建指定目标时附带代码覆盖率检测。这在下面的覆盖率部分有更详细的描述。

go version

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

Cgo

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

此默认更改最重要的影响是,当 Go 安装在没有 C 编译器的系统上时,它现在将对标准库中使用 cgo 的包使用纯 Go 构建,而不是使用预分发的包存档(这些存档已如上所述被移除)或尝试使用 cgo 并失败。这使得 Go 在某些最小化的容器环境以及 macOS 上运行得更好(自 Go 1.16 起,基于 cgo 的包在 macOS 上已不使用预分发的包存档)。

标准库中使用 cgo 的包是 netos/userplugin。在 macOS 上,netos/user 包已被重写为不使用 cgo:相同的代码现在用于 cgo 和非 cgo 构建以及交叉编译构建。在 Windows 上,netos/user 包从未使用过 cgo。在其他系统上,禁用 cgo 的构建将使用这些包的纯 Go 版本。

一个后果是,在 macOS 上,如果使用 -buildmode=c-archive 构建使用 net 包的 Go 代码,将生成的归档文件链接到 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.Formattime.Parse 中使用时间格式 2006-02-01(yyyy-dd-mm)。这种格式并未出现在常见的日期标准中,但在尝试使用 ISO 8601 日期格式(yyyy-mm-dd)时经常被误用。

Runtime

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

垃圾收集器在某些情况下关于 goroutine 辅助的行为变得不那么反复无常。

Go 1.20 添加了一个新的 runtime/coverage 包,其中包含用于从不通过 os.Exit() 终止的长时间运行和/或服务器程序在运行时写入覆盖率配置文件数据的 API。

Compiler {#compiler}

Go 1.20 增加了对配置引导优化(PGO)的预览支持。PGO 使工具链能够基于运行时的配置文件信息执行针对特定应用程序和负载的优化。目前,编译器支持 pprof CPU 配置文件,这些配置文件可以通过常规方式收集,例如使用 runtime/pprofnet/http/pprof 包。要启用 PGO,需要将 pprof 配置文件文件的路径通过 -pgo 标志传递给 go build,如上文所述。Go 1.20 使用 PGO 在热点调用位置更积极地进行函数内联。对一组具有代表性的 Go 程序的基准测试表明,启用配置引导的内联优化可将性能提升约 3–4%。详细文档请参阅 PGO 用户指南。我们计划在未来的版本中添加更多配置引导优化。请注意,配置引导优化目前为预览版,请谨慎使用。

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

编译器现在默认会拒绝匿名接口循环并报错。这类循环源于嵌入接口的复杂用法,且一直存在微妙的正确性问题,但我们没有证据表明它们在实践中实际被使用。假设没有用户因这一更改受到负面影响的报告,我们计划在 Go 1.22 中更新语言规范以正式禁止它们,这样工具作者也可以停止支持它们。

Go 1.18 和 1.19 的构建速度有所下降,这主要是由于添加了泛型支持及后续工作。Go 1.20 将构建速度提高了最多 10%,使其恢复到与 Go 1.17 相当的水平。相对于 Go 1.19,生成的代码性能通常也略有提升。

链接器

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

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

Go 1.20 使用 go:type: 前缀作为编译器生成的符号,而不是 go.type.。这避免了对于名称以 go. 开头的用户包的混淆。 debug/gosym 包能够理解这种为 Go 1.20 及更高版本构建的二进制文件的新命名约定。

引导

从源代码构建 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 工具链的系统)。未来,我们计划大约每年向前推进一次引导工具链,特别地,我们预计 Go 1.22 将需要 Go 1.20 的最终补丁版本进行引导。

标准库

新的 crypto/ecdh 包

Go 1.20 添加了一个新的 crypto/ecdh 包,用于为在 NIST 曲线和 Curve25519 上进行椭圆曲线迪菲-赫尔曼密钥交换提供显式支持。

程序应使用 crypto/ecdh 而不是 crypto/elliptic 中更底层的功能来进行 ECDH 操作,对于更高级的用例,应使用第三方模块。

包装多个错误

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

一个错误 e 可以通过提供一个返回 []errorUnwrap 方法来包装多个错误。

errors.Iserrors.As 函数已更新,以检查多重包装的错误。

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

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

HTTP ResponseController

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

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

ResponseController 类型提供了一种更清晰、更易发现的方式来添加每处理器控制。Go 1.20 中添加的两个此类控制是 SetReadDeadlineSetWriteDeadline,它们允许设置每请求的读写截止时间。例如:

func RequestHandler(w ResponseWriter, r *Request) {
  rc := http.NewResponseController(w)
  rc.SetWriteDeadline(time.Time{}) // 发送大型响应时禁用 Server.WriteTimeout
  io.Copy(w, bigData)
}

新的 ReverseProxy Rewrite 钩子 {#reverseproxy_rewrite}

httputil.ReverseProxy 转发代理新增了一个 Rewrite 钩子函数,取代了之前的 Director 钩子。

Rewrite 钩子接受一个 ProxyRequest 参数, 该参数同时包含代理收到的入站请求和即将发送的出站请求。 与仅操作出站请求的 Director 钩子不同, 这使得 Rewrite 钩子能够避免某些场景下,恶意入站请求可能导致钩子添加的头部信息在转发前被移除的问题。 参见 issue #50580

ProxyRequest.SetURL 方法将出站请求路由到指定的目标地址,并取代了 NewSingleHostReverseProxy 函数。 与 NewSingleHostReverseProxy 不同,SetURL 还会设置出站请求的 Host 头部。

ProxyRequest.SetXForwarded 方法设置出站请求的 X-Forwarded-ForX-Forwarded-HostX-Forwarded-Proto 头部。 当使用 Rewrite 时,默认不会添加这些头部。

一个使用这些特性的 Rewrite 钩子示例如下:

proxyHandler := &httputil.ReverseProxy{
  Rewrite: func(r *httputil.ProxyRequest) {
    r.SetURL(outboundURL) // 将请求转发至 outboundURL。
    r.SetXForwarded()     // 设置 X-Forwarded-* 头部。
    r.Out.Header.Set("X-Additional-Header", "由代理设置的头部")
  },
}

当入站请求没有 User-Agent 头部时, ReverseProxy 不再为转发的请求添加该头部。

库的次要变更

一如既往,库中存在各种次要变更和更新, 这些都是基于 Go 1 兼容性承诺 进行的。 还有一些性能改进,此处未逐一列出。

archive/tar

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

archive/zip

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

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

bytes

新增的 CutPrefixCutSuffix 函数 类似于 TrimPrefixTrimSuffix, 但还会报告字符串是否被修剪。

新增的 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.SHA512Options.HashFunc 来指示。 它们现在还支持带有上下文的 Ed25519ctx 和 Ed25519ph, 这通过设置新的 Options.Context 字段来指示。

crypto/rsa

新增的字段 OAEPOptions.MGFHash 允许为 OAEP 解密单独配置 MGF1 哈希。 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

ParsePKCS8PrivateKeyMarshalPKCS8PrivateKey 现在支持 *crypto/ecdh.PrivateKey 类型的密钥。ParsePKIXPublicKeyMarshalPKIXPublicKey 现在支持 *crypto/ecdh.PublicKey 类型的密钥。解析 NIST 曲线密钥仍然返回 *ecdsa.PublicKey*ecdsa.PrivateKey 类型的值。使用它们新的 ECDH 方法可以转换为 crypto/ecdh 类型。

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

debug/elf

尝试使用 Section.DataSection.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

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

encoding/xml

新增的 Encoder.Close 方法可用于在编码完成时检查是否存在未关闭的元素。

解码器现在拒绝包含多个冒号的元素和属性名称(例如 <a:b:c>),以及解析为空字符串的命名空间(例如 xmlns:a="")。

解码器现在拒绝在开始标签和结束标签中使用不同命名空间前缀的元素,即使这些前缀表示的是同一个命名空间。

errors

新增的 Join 函数返回一个包装了错误列表的错误。

fmt

Errorf 函数现在支持多次使用 %w 格式化动词,返回的错误可以展开为所有 %w 参数的列表。

新增的 FormatString 函数可以恢复与 State 对应的格式化指令,这在 Formatter 实现中可能很有用。

go/ast

新增的 RangeStmt.Range 字段记录了范围语句中 range 关键字的位置。

新增的 File.FileStartFile.FileEnd 字段记录了整个源文件的起始和结束位置。

go/token

新增的 FileSet.RemoveFile 方法可从 FileSet 中移除一个文件。 长时间运行的程序可使用此方法来释放与不再需要的文件相关联的内存。

go/types

新增的 Satisfies 函数报告一个类型是否满足某个约束。 此变更与新的语言语义保持一致,该语义区分了满足约束与实现接口。

html/template

Go 1.20.3 及更高版本 禁止在 ECMAScript 6 模板字面量中执行动作。 此行为可通过设置 GODEBUG=jstmpllitinterp=1 来恢复。

io

新增的 OffsetWriter 包装了一个底层的 WriterAt,并提供 SeekWriteWriteAt 方法,这些方法会将其有效的文件偏移位置调整一个固定量。

io/fs

新增的错误值 SkipAll 可立即并成功地终止一个 WalkDir 操作。

math/big

math/big 包的广泛作用域和依赖于输入的执行时间使其不适合用于实现加密算法。 标准库中的加密包不再在攻击者控制的输入上调用非平凡的 Int 方法。 未来,math/big 中的某个错误是否被视为安全漏洞,将取决于它对标准库更广泛的影响。

math/rand

math/rand 包现在会自动用一个随机值为全局随机数生成器(供 Float64Int 等顶层函数使用)设定种子,并且顶层的 Seed 函数已被弃用。 需要可重复随机数序列的程序应优先考虑分配自己的随机源,使用 rand.New(rand.NewSource(seed))

需要早期一致的全局播种行为的程序,可以在其环境中设置 GODEBUG=randautoseed=0

顶层的 Read 函数已被弃用。 在几乎所有情况下,使用 crypto/rand.Read 更为合适。

mime

ParseMediaType 函数现在允许重复的参数名称,只要这些名称的值相同即可。

mime/multipart

Reader 类型的方法现在会包装底层 io.Reader 返回的错误。

在 Go 1.19.8 及更高版本中,此包设置了对所处理 MIME 数据大小的限制,以防御恶意输入。 Reader.NextPartReader.NextRawPart 将一个部分(part)中的头部数量限制为 10000,Reader.ReadForm 将所有 FileHeaders 中的头部总数限制为 10000。 这些限制可通过 GODEBUG=multipartmaxheaders 设置进行调整。 Reader.ReadForm 进一步将表单中的部分数量限制为 1000。 此限制可通过 GODEBUG=multipartmaxparts 设置进行调整。

net

LookupCNAME 函数现在在存在 CNAME 记录时始终返回其内容。之前在 Unix 系统和使用纯 Go 解析器时,如果 CNAME 记录指向一个没有 AAAAACNAME 记录的名称,LookupCNAME 会返回错误。此更改使 LookupCNAME 行为与 Windows 上之前的行为保持一致,允许 LookupCNAME 在存在 CNAME 时就成功返回。

Interface.Flags 现在包含新的标志 FlagRunning,表示一个处于操作活跃状态的接口。一个已被管理配置但未激活(例如,因为网线未连接)的接口将设置 FlagUp 但不设置 FlagRunning

新增的 Dialer.ControlContext 字段包含一个回调函数,其作用类似于现有的 Dialer.Control 钩子,但额外接受拨号上下文作为参数。 当 ControlContext 不为 nil 时,将忽略 Control

Go DNS 解析器识别 trust-ad 解析器选项。 当在 resolv.conf 中设置了 options trust-ad 时,Go 解析器将在 DNS 查询中设置 AD 位。解析器不会利用响应中的 AD 位。

DNS 解析现在会检测 /etc/nsswitch.conf 文件的更改,并在更改时重新加载该文件。检查间隔最多为五秒一次,这与之前对 /etc/hosts/etc/resolv.conf 的处理方式保持一致。

net/http

ResponseWriter.WriteHeader 函数现在支持发送 1xx 状态码。

新增的 Server.DisableGeneralOptionsHandler 配置设置允许禁用默认的 OPTIONS * 处理器。 新增的 Transport.OnProxyConnectResponse 钩子函数会在 Transport 从代理收到针对 CONNECT 请求的 HTTP 响应时被调用。

HTTP 服务器现在接受包含消息体的 HEAD 请求,而不是将其视为无效请求而拒绝。

net/http 函数返回的 HTTP/2 流错误现在可以使用 errors.As 转换为 golang.org/x/net/http2.StreamError

现在会裁剪 Cookie 名称中的前导和尾随空格,而不是将其视为无效。例如,现在接受将 Cookie 设置为 "name =value",并将其视为设置名为 "name" 的 Cookie。

现在认为 Expires 字段为空的 Cookie 是有效的。Cookie.Valid 仅在 Expires 字段被设置时才对其进行检查。

net/netip

新增的 IPv6LinkLocalAllRoutersIPv6Loopback 函数是 net.IPv6loopbacknet.IPv6linklocalallroutersnet/netip 等效形式。

os

在 Windows 上,名称 NULMkdirStat 中不再被视为特殊情况。

在 Windows 上,当文件是目录时,File.Stat 现在使用文件句柄来检索属性。此前,它使用的是传递给 Open 的路径,如果文件已被移动或替换,该路径可能不再代表文件句柄对应的文件。此更改修改了 Open 以在不使用 FILE_SHARE_DELETE 访问权限的情况下打开目录,这与普通文件的行为一致。

在 Windows 上,File.Seek 现在支持将目录的偏移量定位到开头。

os/exec

新增的 Cmd 字段 CancelWaitDelay 指定了当 Cmd 关联的 Context 被取消或其进程退出时,但子进程仍持有 I/O 管道打开时 Cmd 的行为。

path/filepath

新增的错误 SkipAll 会立即终止 Walk,但视为成功结束。

新增的 IsLocal 函数用于报告一个路径在语法上是否相对于某个目录是局部的。例如,如果 IsLocal(p)true,则 Open(p) 将指向一个在语法上位于以当前目录为根的子树内的文件。

reflect

新增的 Value.ComparableValue.Equal 方法可用于比较两个 Value 是否相等。Comparable 报告 Equal 对于给定的 Value 接收者是否为有效操作。

新增的 Value.Grow 方法用于扩展切片,以保证为另外 n 个元素提供空间。

新增的 Value.SetZero 方法将值设置为其类型的零值。

Go 1.18 引入了 Value.SetIterKeyValue.SetIterValue 方法。这些是优化版本:v.SetIterKey(it) 等同于 v.Set(it.Key())。这些实现不正确地省略了对未导出字段使用的检查,而未优化的版本中存在此检查。Go 1.20 修正了这些方法,加入了未导出字段检查。

regexp

Go 1.19.2 和 Go 1.18.7 包含了对正则表达式解析器的安全修复,使其拒绝会消耗过多内存的大型表达式。由于 Go 补丁版本不引入新的 API,解析器在这种情况下返回 syntax.ErrInternalError。Go 1.20 添加了一个更具体的错误 syntax.ErrLarge,解析器现在返回此错误来代替。

runtime/cgo

Go 1.20 新增了 Incomplete 标记类型。cgo 生成的代码将使用 cgo.Incomplete 来标记不完整的 C 类型。

runtime/metrics

Go 1.20 新增了支持的指标,包括当前的 GOMAXPROCS 设置 (/sched/gomaxprocs:threads)、执行的 cgo 调用次数 (/cgo/go-to-c-calls:calls)、互斥锁总阻塞时间 (/sync/mutex/wait/total:seconds) 以及在垃圾回收上花费的时间的各种度量。

基于时间的直方图指标现在精度较低,但占用的内存少得多。

runtime/pprof

互斥锁配置文件样本现在是预先缩放的,修复了一个问题,即如果执行期间采样率发生变化,旧的互斥锁配置文件样本会被错误地缩放。 在 Windows 上收集的配置文件现在包含内存映射信息,这修复了位置无关二进制文件的符号化问题。

runtime/trace

垃圾回收器的后台清扫器现在让出(yield)频率降低,导致执行跟踪中的额外事件大大减少。

strings

新的 CutPrefixCutSuffix 函数 类似于 TrimPrefixTrimSuffix, 但还会报告字符串是否被修剪。

sync

新的 Map 方法 SwapCompareAndSwapCompareAndDelete 允许以原子方式更新现有的映射条目。

syscall

在 FreeBSD 上,已移除兼容 FreeBSD 11 及更早版本所需的垫片。

在 Linux 上,定义了额外的 CLONE_* 常量,用于 SysProcAttr.Cloneflags 字段。

在 Linux 上,新的 SysProcAttr.CgroupFDSysProcAttr.UseCgroupFD 字段提供了一种将子进程置于特定 cgroup 中的方法。

testing

新方法 B.Elapsed 报告基准测试当前的经过时间,这对于计算使用 ReportMetric 报告的速率可能很有用。

从传递给 T.Cleanup 的函数中调用 T.Run 从未有明确定义,现在将引发恐慌(panic)。

time

新的时间布局常量 DateTimeDateOnlyTimeOnly 为在公共 Go 源代码调查中最常用的三种布局字符串提供了名称。

新的 Time.Compare 方法比较两个时间。

Parse 现在会忽略输入中的亚纳秒精度,而不是将这些数字报告为错误。

Time.MarshalJSON 方法现在对遵循 RFC 3339 的要求更加严格。

unicode/utf16

新的 AppendRune 函数将给定 rune 的 UTF-16 编码附加到 uint16 切片上,类似于 utf8.AppendRune