Go 1.23 发布说明

Go 1.23 简介

最新的 Go 版本 1.23 在 Go 1.22 发布六个月后到来。其大部分更改都在工具链、运行时和库的实现中。一如既往,此版本保持了 Go 1 的兼容性承诺。我们预计几乎所有 Go 程序都将像以前一样继续编译和运行。

语言变化

“for-range” 循环中的 “range” 子句现在接受以下类型的迭代器函数

func(func() bool)
func(func(K) bool)
func(func(K, V) bool)

作为 range 表达式。迭代器参数函数的调用会生成 “for-range” 循环的迭代值。有关详细信息,请参阅 iter 包文档、语言规范“函数类型上的 Range” 博客文章。有关动机,请参阅 2022 年的“range-over-func” 讨论

Go 1.23 包括对泛型类型别名的预览支持。使用 GOEXPERIMENT=aliastypeparams 构建工具链可在包内启用此功能。(目前尚不支持跨包边界使用泛型别名类型。)

工具

遥测

从 Go 1.23 开始,Go 工具链可以收集使用情况和故障统计信息,帮助 Go 团队了解 Go 工具链的使用方式及其运行情况。我们将这些统计信息称为Go 遥测

Go 遥测是一个选择加入系统,由go telemetry 命令控制。默认情况下,工具链程序会将统计信息收集到计数器文件中,这些文件可以在本地检查,但否则不会使用(go telemetry local)。

为了帮助我们保持 Go 的良好运行并了解 Go 的使用情况,请考虑运行 go telemetry on 来选择加入 Go 遥测。在此模式下,匿名计数器报告将每周上传到 telemetry.go.dev,在那里它们将被汇总成图表,并提供给任何想要分析数据的 Go 贡献者或用户下载。有关 Go 遥测系统的更多详细信息,请参阅“Go 遥测”。

Go 命令

设置 GOROOT_FINAL 环境变量不再有任何作用(#62047)。将 go 命令安装到 $GOROOT/bin/go 以外位置的发行版应安装符号链接,而不是重新定位或复制 go 二进制文件。

新的 go env -changed 标志会导致命令仅打印其有效值与在空环境中未事先使用 -w 标志获得的默认值不同的设置。

新的 go mod tidy -diff 标志会导致命令不修改文件,而是将必要的更改打印为统一的 diff。如果需要更新,它将以非零代码退出。

go list -m -json 命令现在包含新的 SumGoModSum 字段。这与 go mod download -json 命令的现有行为类似。

go.modgo.work 中的新 godebug 指令声明了一个适用于所用工作模块或工作区的 GODEBUG 设置

Vet

go vet 子命令现在包括 stdversion 分析器,该分析器标记引用文件中生效的 Go 版本过旧的符号。(有效版本由文件封闭 go.mod 文件中的 go 指令以及文件中的任何 //go:build 约束决定。)

例如,它将报告一个模块文件中对 reflect.TypeFor 函数(在 go1.22 中引入)的引用诊断,该模块的 go.mod 文件指定 go 1.21

Cgo

cmd/cgo 支持新的 -ldflags 标志,用于将标志传递给 C 链接器。go 命令会自动使用它,避免在 CGO_LDFLAGS 非常大时出现“参数列表过长”错误。

Trace

trace 工具现在能更好地容忍部分损坏的跟踪,它会尝试恢复可以恢复的跟踪数据。此功能在查看程序崩溃期间收集的跟踪时特别有用,因为在大多数情况下,导致崩溃的跟踪数据现在将可恢复

运行时

运行时在未处理的 panic 或其他致命错误后打印的跟踪现在将错误消息的第二行和后续行(例如,panic 的参数)缩进一个制表符,以便可以将其与第一个 goroutine 的堆栈跟踪明确区分开来。有关讨论,请参阅 #64590

编译器

使用配置文件引导优化进行构建的构建时间开销已显着降低。以前,大型构建在启用 PGO 后可能会导致构建时间增加 100% 以上。在 Go 1.23 中,开销应为个位数百分比。

Go 1.23 中的编译器现在可以重叠函数中不相交区域中访问的局部变量的堆栈帧槽,从而减少 Go 应用程序的堆栈使用。

对于 386 和 amd64,编译器将使用 PGO 中的信息来对齐循环中某些热块。这会额外将性能提高 1-1.5%,但会增加 0.1% 的文本和二进制大小。目前这仅在 386 和 amd64 上实现,因为它在其他平台上没有显示出改进。可以使用 -gcflags=[=-d=alignhot=0 禁用热块对齐。

链接器

链接器现在禁止使用 //go:linkname 指令引用标准库(包括运行时)中未在其定义上标记为 //go:linkname 的内部符号。同样,链接器禁止汇编代码引用此类符号。为了向后兼容,在大型开源代码库中发现的现有 //go:linkname 用法仍然受支持。任何对标准库内部符号的新引用都将被禁止。

链接器命令行标志 -checklinkname=0 可用于禁用此检查,用于调试和实验目的。

构建动态链接的 ELF 二进制文件(包括 PIE 二进制文件)时,新的 -bindnow 标志启用即时函数绑定。

标准库

计时器更改

Go 1.23 对 time.Timertime.Ticker 的实现进行了两项重大更改。

首先,不再被程序引用的 TimerTicker 会立即符合垃圾回收条件,即使它们的 Stop 方法尚未被调用。早期版本的 Go 不会在未停止的 Timer 触发之前收集它们,并且从不收集未停止的 Ticker

其次,与 TimerTicker 关联的计时器通道现在是无缓冲的,容量为 0。此更改的主要影响是 Go 现在保证对于对 ResetStop 方法的任何调用,在该调用之前准备的任何陈旧值都不会在该调用之后发送或接收。早期版本的 Go 使用带有一个元素缓冲区的通道,这使得正确使用 ResetStop 变得困难。此更改的一个可见效果是计时器通道的 lencap 现在返回 0 而不是 1,这可能会影响轮询长度以决定计时器通道上的接收是否成功的程序。此类代码应使用非阻塞接收。

这些新行为仅在主 Go 程序位于其 go.mod go 行使用 Go 1.23.0 或更高版本的模块中时才启用。当 Go 1.23 构建旧程序时,旧行为仍然有效。新的 GODEBUG 设置 asynctimerchan=1 可用于即使程序在其 go.mod 文件中指定 Go 1.23.0 或更高版本时,也恢复为异步通道行为。

新的 unique 包

新的 unique 包提供了规范化值(例如“内部化”或“哈希一致”)的功能。

任何可比较类型的值都可以通过新的 Make[T] 函数进行规范化,该函数以 Handle[T] 的形式生成对值的规范副本的引用。当且仅当用于生成句柄的值相等时,两个 Handle[T] 才相等,从而允许程序去重值并减少其内存占用。比较两个 Handle[T] 值是高效的,可以简化为简单的指针比较。

迭代器

新的 iter 包提供了用于处理用户定义迭代器的基本定义。

slices 包添加了几个与迭代器一起使用的函数

  • All 返回切片索引和值的迭代器。
  • Values 返回切片元素的迭代器。
  • Backward 返回一个反向循环切片的迭代器。
  • Collect 从迭代器中收集值到一个新切片中。
  • AppendSeq 将迭代器中的值附加到现有切片。
  • Sorted 从迭代器中收集值到一个新切片中,然后对切片进行排序。
  • SortedFunc 类似于 Sorted 但带有比较函数。
  • SortedStableFunc 类似于 SortFunc 但使用稳定排序算法。
  • Chunk 返回切片中最多 n 个元素的连续子切片的迭代器。

maps 包添加了几个与迭代器一起使用的函数

  • All 返回映射中的键值对的迭代器。
  • Keys 返回映射中键的迭代器。
  • Values 返回映射中值的迭代器。
  • Insert 将迭代器中的键值对添加到现有映射。
  • Collect 从迭代器中收集键值对到一个新映射中并返回它。

新的 structs 包

新的 structs 包提供了用于结构体字段的类型,这些字段修改了包含结构体类型的属性,例如内存布局。

在此版本中,唯一此类类型是 HostLayout,它指示具有该类型字段的结构体具有符合主机平台期望的布局。HostLayout 应该用于传递给、从主机 API 返回或通过指向主机 API 的指针访问的类型。如果没有此标记,语言规范不保证结构体布局顺序,尽管自 Go 1.23 起,主机和语言布局恰好匹配。

对库的微小更改

archive/tar

如果传递给 FileInfoHeader 的参数实现了新的 FileInfoNames 接口,则将使用接口方法设置文件头的 Uname/Gname。这允许应用程序覆盖系统相关的 Uname/Gname 查找。

crypto/tls

TLS 客户端现在支持加密客户端 Hello 草案规范。可以通过将 Config.EncryptedClientHelloConfigList 字段设置为要连接到的主机的编码 ECHConfigList 来启用此功能。

QUIC 实现使用的 QUICConn 类型包括报告会话恢复状态的新事件,并提供了一种 QUIC 层将数据添加到会话票证和会话缓存条目的方法。

Config.CipherSuites 为 nil 时,3DES 密码套件已从默认列表中删除。可以通过将 tls3des=1 添加到 GODEBUG 环境变量来恢复默认设置。

Config.CurvePreferences 为 nil 时,实验性的后量子密钥交换机制 X25519Kyber768Draft00 现在默认启用。可以通过将 tlskyber=0 添加到 GODEBUG 环境变量来恢复默认设置。这在处理无法正确处理大记录的错误 TLS 服务器时可能很有用,导致握手期间超时(请参阅 TLS 后量子 TL;DR 失败)。

Go 1.23 更改了 X509KeyPairLoadX509KeyPair 的行为,以填充返回的 CertificateCertificate.Leaf 字段。为此行为添加了新的 x509keypairleaf GODEBUG 设置

crypto/x509

CreateCertificateRequest 现在正确支持 RSA-PSS 签名算法。

CreateCertificateRequestCreateRevocationList 现在使用签名者的公钥验证生成的签名。如果签名无效,则返回错误。自 Go 1.16 以来,这一直是 CreateCertificate 的行为。

x509sha1 GODEBUG 设置将在下一个 Go 主要版本(Go 1.24)中删除。这意味着 crypto/x509 将不再支持验证使用基于 SHA-1 的签名算法的证书上的签名。

新的 ParseOID 函数解析点编码的 ASN.1 对象标识符字符串。OID 类型现在实现了 encoding.BinaryMarshalerencoding.BinaryUnmarshalerencoding.TextMarshalerencoding.TextUnmarshaler 接口。

database/sql

现在,由 driver.Valuer 实现返回的错误被包装起来,以便在 DB.QueryDB.ExecDB.QueryRow 等操作期间改进错误处理。

debug/elf

debug/elf 包现在定义了 PT_OPENBSD_NOBTCFI。此 ProgType 用于在 OpenBSD 二进制文件上禁用分支跟踪控制流完整性 (BTCFI) 强制执行。

现在定义了符号类型常量 STT_RELCSTT_SRELCSTT_GNU_IFUNC

encoding/binary

新的 EncodeDecode 函数是 ReadWrite 的字节切片等效项。Append 允许将多个数据封送(marshal)到同一个字节切片中。

go/ast

新的 Preorder 函数返回一个方便的迭代器,遍历语法树的所有节点。

go/types

Func 类型表示函数或方法符号,现在有一个 Func.Signature 方法,该方法返回函数的类型,该类型始终是 Signature

Alias 类型现在有一个 Rhs 方法,该方法返回其声明右侧的类型:给定 type A = B,A 的 Rhs 是 B。(#66559

已添加方法 Alias.OriginAlias.SetTypeParamsAlias.TypeParamsAlias.TypeArgs。它们是泛型别名类型所需的。

默认情况下,go/types 现在为类型别名生成 Alias 类型节点。此行为可以通过 GODEBUG gotypesalias 标志控制。其默认值已从 Go 1.22 中的 0 更改为 Go 1.23 中的 1。

math/rand/v2

已添加 Uint 函数和 Rand.Uint 方法。它们在 Go 1.22 中被无意中遗漏了。

新的 ChaCha8.Read 方法实现了 io.Reader 接口。

net

新的类型 KeepAliveConfig 允许通过新的 TCPConn.SetKeepAliveConfig 方法以及 DialerListenConfig 的新 KeepAliveConfig 字段,对 TCP 连接的保活选项进行微调。

DNSError 类型现在包装由超时或取消引起的错误。例如,errors.Is(someDNSErr, context.DeadlineExceeded) 现在将报告 DNS 错误是否由超时引起。

新的 GODEBUG 设置 netedns0=0 禁用在 DNS 请求上发送 EDNS0 附加头,因为据报道它们会破坏某些调制解调器上的 DNS 服务器。

net/http

Cookie 现在保留 cookie 值周围的双引号。新的 Cookie.Quoted 字段指示 Cookie.Value 最初是否被引用。

新的 Request.CookiesNamed 方法检索所有与给定名称匹配的 cookie。

新的 Cookie.Partitioned 字段标识具有 Partitioned 属性的 cookie。

ServeMux 使用的模式现在允许方法名后有一个或多个空格或制表符。以前只允许一个空格。

新的 ParseCookie 函数解析 Cookie 头部值并返回其中设置的所有 cookie。由于相同的 cookie 名称可以出现多次,因此返回的 Values 可以包含给定键的多个值。

新的 ParseSetCookie 函数解析 Set-Cookie 头部值并返回一个 cookie。如果存在语法错误,则返回错误。

ServeContentServeFileServeFileFS 在提供错误时现在会移除 Cache-ControlContent-EncodingEtagLast-Modified 头部。这些头部通常适用于非错误内容,而不适用于错误文本。

包装 ResponseWriter 并应用即时编码(例如 Content-Encoding: gzip)的中间件在此更改后将无法工作。ServeContentServeFileServeContent 处理 Range 请求时,改变所服务内容大小(例如通过压缩)的中间件已经无法正常工作。即时压缩应使用 Transfer-Encoding 头部而不是 Content-Encoding

对于入站请求,新的 Request.Pattern 字段包含与请求匹配的 ServeMux 模式(如果有)。当设置 GODEBUG=httpmuxgo121=1 时,此字段未设置。

net/http/httptest

新的 NewRequestWithContext 方法使用 context.Context 创建一个传入请求。

net/netip

在 Go 1.22 及更早版本中,使用 reflect.DeepEqual 比较包含 IPv4 地址的 Addr 与包含该地址的 IPv4 映射 IPv6 形式的 Addr.Compare 比较时不同。此错误现已修复,所有三种方法现在都报告相同的结果。

os

Stat 函数现在为 Windows 上作为 Unix 套接字的文件设置 ModeSocket 位。这些文件通过其重新解析标签设置为 IO_REPARSE_TAG_AF_UNIX 来标识。

在 Windows 上,LstatStat 报告的重新解析点的模式位发生了变化。挂载点不再设置 ModeSymlink,并且不是符号链接、Unix 套接字或去重文件的重新解析点现在始终设置 ModeIrregular。此行为由 winsymlink 设置控制。对于 Go 1.23,它默认为 winsymlink=1。以前的版本默认为 winsymlink=0

CopyFS 函数将 io/fs.FS 复制到本地文件系统。

在 Windows 上,Readlink 不再尝试将卷规范化为驱动器号,这并非总是可能的。此行为由 winreadlinkvolume 设置控制。对于 Go 1.23,它默认为 winreadlinkvolume=1。以前的版本默认为 winreadlinkvolume=0

在支持 pidfd 的 Linux 上(通常是 Linux v5.4+),Process 相关函数和方法内部使用 pidfd(而不是 PID),消除了当操作系统重用 PID 时可能发生的错误定位。Pidfd 支持对用户完全透明,除了进程可能拥有的额外进程文件描述符。

path/filepath

新的 Localize 函数安全地将斜杠分隔的路径转换为操作系统路径。

在 Windows 上,EvalSymlinks 不再评估挂载点,这是许多不一致和错误的来源。此行为由 winsymlink 设置控制。对于 Go 1.23,它默认为 winsymlink=1。以前的版本默认为 winsymlink=0

在 Windows 上,EvalSymlinks 不再尝试将卷规范化为驱动器号,这并非总是可能的。此行为由 winreadlinkvolume 设置控制。对于 Go 1.23,它默认为 winreadlinkvolume=1。以前的版本默认为 winreadlinkvolume=0

reflect

Value 中同名方法同义的新方法已添加到 Type

  1. Type.OverflowComplex
  2. Type.OverflowFloat
  3. Type.OverflowInt
  4. Type.OverflowUint

新的 SliceAt 函数类似于 NewAt,但用于切片。

Value.PointerValue.UnsafePointer 方法现在支持 String 类型的 值。

新的方法 Value.SeqValue.Seq2 返回序列,这些序列遍历值,就像它在 for/range 循环中使用一样。新的方法 Type.CanSeqType.CanSeq2 分别报告调用 Value.SeqValue.Seq2 是否会在不 panic 的情况下成功。

runtime/debug

SetCrashOutput 函数允许用户指定一个备用文件,运行时应将其致命崩溃报告写入该文件。它可用于为所有意外崩溃构建自动化报告机制,而不仅仅是那些明确使用 recover 的 goroutine 中的崩溃。

runtime/pprof

allocmutexblockthreadcreategoroutine 配置文件的最大堆栈深度已从 32 帧提高到 128 帧。

runtime/trace

当程序因未捕获的 panic 而崩溃时,运行时现在会显式刷新跟踪数据。这意味着,如果在跟踪活动期间程序崩溃,跟踪中将提供更完整的跟踪数据。

slices

Repeat 函数返回一个新切片,该切片重复提供的切片给定次数。

sync

Map.Clear 方法删除所有条目,导致 Map 为空。它类似于 clear

sync/atomic

新的 AndOr 运算符对给定输入应用按位 ANDOR,并返回旧值。

syscall

syscall 包现在在 Windows 上定义了 WSAENOPROTOOPT

GetsockoptInt 函数现在在 Windows 上受支持。

testing/fstest

TestFS 现在返回一个结构化错误,该错误可以解包(通过方法 Unwrap() []error)。这允许使用 errors.Iserrors.As 检查错误。

text/template

模板现在支持新的“else with”操作,这在某些用例中可以降低模板复杂性。

time

ParseParseInLocation 现在会在时区偏移超出范围时返回错误。

在 Windows 上,TimerTicker 以及使 goroutine 休眠的函数(例如 Sleep)的时间分辨率已从 15.6ms 提高到 0.5ms。

unicode/utf16

RuneLen 函数返回 rune 的 UTF-16 编码中的 16 位字数。如果 rune 不是有效的 UTF-16 编码值,则返回 -1。

移植

Darwin

正如 Go 1.22 发布说明中宣布的那样,Go 1.23 要求 macOS 11 Big Sur 或更高版本;对以前版本的支持已停止。

Linux

Go 1.23 是最后一个需要 Linux 内核版本 2.6.32 或更高版本的版本。Go 1.24 将需要 Linux 内核版本 3.2 或更高版本。

OpenBSD

Go 1.23 添加了对 64 位 RISC-V 上的 OpenBSD 的实验性支持(GOOS=openbsd, GOARCH=riscv64)。

ARM64

Go 1.23 引入了新的 GOARM64 环境变量,该变量在编译时指定 ARM64 架构的最低目标版本。允许的值为 v8.{0-9}v9.{0-5}。这后面可以跟一个指定目标硬件实现的扩展的选项。有效选项为 ,lse,crypto

GOARM64 环境变量默认为 v8.0

RISC-V

Go 1.23 引入了一个新的 GORISCV64 环境变量,用于选择要编译的 RISC-V 用户模式应用程序配置文件。允许的值为 rva20u64rva22u64

GORISCV64 环境变量默认为 rva20u64

Wasm

GOROOT/misc/wasm 中的 go_wasip1_wasm_exec 脚本已停止支持 wasmtime < 14.0.0 的版本。