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 over Function Types 博客文章。动机请参阅 2022 年的“range-over-func” 讨论。
Go 1.23 包含对泛型类型别名的预览支持。使用 GOEXPERIMENT=aliastypeparams
构建工具链可在包内启用此特性。(目前尚不支持跨包边界使用泛型别名类型。)
工具
遥测
从 Go 1.23 开始,Go 工具链可以收集使用情况和故障统计信息,帮助 Go 团队了解 Go 工具链的使用方式和工作状况。我们将这些统计信息称为Go 遥测。
Go 遥测是一个选择加入(opt-in)系统,由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
标志使得该命令不修改文件,而是将必要的更改以统一差异(unified diff)形式打印出来。如果需要更新,它将以非零代码退出。
go
list
-m
-json
命令现在包含新的 Sum
和 GoModSum
字段。这类似于现有的 go
mod
download
-json
命令的行为。
go.mod
和 go.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 或其他致命错误后打印的回溯(traceback)现在会将错误消息的第二行及后续行(例如 panic 的参数)缩进一个制表符,以便与第一个 goroutine 的堆栈跟踪明确区分。请参阅#64590 了解讨论。
编译器
使用配置文件引导优化(PGO)构建的构建时间开销已显著降低。之前,大型构建启用 PGO 可能导致构建时间增加 100% 以上。在 Go 1.23 中,开销应在个位数百分比。
Go 1.23 中的编译器现在可以重叠函数中不相交区域访问的局部变量的堆栈帧槽,从而减少 Go 应用程序的堆栈使用。
对于 386 和 amd64 架构,编译器将使用 PGO 的信息来对齐循环中的某些热点块(hot blocks)。这以额外的 0.1% 文本和二进制大小为代价,将性能额外提高了 1-1.5%。目前仅在 386 和 amd64 上实现此功能,因为它在其他平台上未显示出改进。可以使用 -gcflags=[<packages>=]-d=alignhot=0
禁用热点块对齐。
链接器
链接器现在禁止使用 //go:linkname
指令引用标准库(包括运行时)中未在其定义中标记 //go:linkname
的内部符号。类似地,链接器也禁止汇编代码引用此类符号。为了向后兼容,大型开源代码库中已有的 //go:linkname
用法仍然受支持。任何新的标准库内部符号引用将被禁止。
可以使用链接器命令行标志 -checklinkname=0
禁用此检查,用于调试和实验目的。
构建动态链接的 ELF 二进制文件(包括 PIE 二进制文件)时,新的 -bindnow
标志会启用即时函数绑定。
标准库
定时器变化
Go 1.23 对 time.Timer
和 time.Ticker
的实现做了两项重要更改。
首先,程序不再引用的 Timer
和 Ticker
立即符合垃圾回收的条件,即使尚未调用其 Stop
方法。早期版本的 Go 在未停止的 Timer
触发后才进行回收,而未停止的 Ticker
则从不回收。
其次,与 Timer
或 Ticker
关联的定时器通道现在是无缓冲的,容量为 0。此更改的主要影响是 Go 现在保证,对于对 Reset
或 Stop
方法的任何调用,在该调用之前准备的任何过时值都不会在该调用之后发送或接收。早期版本的 Go 使用带有一个元素的缓冲通道,使得正确使用 Reset
和 Stop
变得困难。此更改的一个可见影响是,定时器通道的 len
和 cap
现在返回 0 而不是 1,这可能会影响那些通过轮询长度来决定是否能成功接收定时器通道上值的程序。此类代码应改用非阻塞接收。
这些新行为仅当主 Go 程序位于其 go.mod
文件中指定 Go 1.23.0 或更高版本的模块中时才会启用。当 Go 1.23 构建旧程序时,旧行为仍然有效。即使程序在其 go.mod
文件中指定 Go 1.23.0 或更高版本,也可以使用新的 GODEBUG 设置 asynctimerchan=1
恢复到异步通道行为。
新的 unique 包
新的 unique
包提供了规范化值(例如“interning”或“hash-consing”)的工具。
任何可比较类型的值都可以使用新的 Make[T]
函数进行规范化,该函数以 Handle[T]
的形式产生对值规范副本的引用。当且仅当用于产生 handle 的值相等时,两个 Handle[T]
才相等,这允许程序去重值并减少其内存占用。比较两个 Handle[T]
值是高效的,最终简化为简单的指针比较。
迭代器
新的 iter
包提供了处理用户定义迭代器的基本定义。
slices
包添加了几个与迭代器协同工作的函数
- All 返回一个遍历切片索引和值的迭代器。
- Values 返回一个遍历切片元素的迭代器。
- Backward 返回一个反向遍历切片的迭代器。
- Collect 将迭代器中的值收集到一个新的切片中。
- AppendSeq 将迭代器中的值附加到现有切片中。
- Sorted 将迭代器中的值收集到一个新的切片中,然后对切片进行排序。
- SortedFunc 类似于
Sorted
,但使用比较函数。 - SortedStableFunc 类似于
SortFunc
但使用稳定排序算法。 - Chunk 返回一个遍历切片中最多 n 个元素的连续子切片的迭代器。
maps
包添加了几个与迭代器协同工作的函数
- All 返回一个遍历 map 中键值对的迭代器。
- Keys 返回一个遍历 map 中键的迭代器。
- Values 返回一个遍历 map 中值的迭代器。
- Insert 将迭代器中的键值对添加到现有 map 中。
- Collect 将迭代器中的键值对收集到一个新的 map 中并返回。
新的 structs 包
新的 structs
包为结构体字段提供了类型,这些类型可以修改包含结构体类型的属性,例如内存布局。
在此版本中,唯一此类类型是 HostLayout
,它表示具有该类型字段的结构体的布局符合主机平台预期。HostLayout 应在通过指针传递给主机 API、从主机 API 返回或通过指针访问的类型中使用。如果没有此标记,语言规范不保证结构体布局顺序,尽管从 Go 1.23 开始,主机和语言布局恰好匹配。
库中的小改动
archive/tar
如果 FileInfoHeader
的参数实现了新的 FileInfoNames
接口,则将使用接口方法设置文件头的 Uname/Gname。这允许应用程序覆盖依赖于系统的 Uname/Gname 查找。
crypto/tls
TLS 客户端现在支持 Encrypted Client Hello 草案规范。通过将 Config.EncryptedClientHelloConfigList
字段设置为正在连接的主机的编码 ECHConfigList,可以启用此功能。
QUIC 实现使用的 QUICConn
类型包含报告会话恢复状态的新事件,并提供了一种让 QUIC 层将数据添加到会话票据和会话缓存条目的方式。
当 Config.CipherSuites
为 nil 时,默认列表中移除了 3DES 密码套件。可以通过向 GODEBUG 环境变量添加 tls3des=1
来恢复默认设置。
当 Config.CurvePreferences
为 nil 时,实验性的后量子密钥交换机制 X25519Kyber768Draft00 现在默认启用。可以通过向 GODEBUG 环境变量添加 tlskyber=0
来恢复默认设置。这在处理无法正确处理大记录的错误 TLS 服务器时很有用,因为它可能导致握手期间超时(请参阅 TLS post-quantum TL;DR fail)。
Go 1.23 更改了 X509KeyPair
和 LoadX509KeyPair
的行为,以填充返回的 Certificate
的 Certificate.Leaf
字段。添加了新的 x509keypairleaf
GODEBUG 设置来控制此行为。
crypto/x509
CreateCertificateRequest
现在正确支持 RSA-PSS 签名算法。
CreateCertificateRequest
和 CreateRevocationList
现在使用签名者的公钥验证生成的签名。如果签名无效,则返回错误。自 Go 1.16 以来,CreateCertificate
的行为一直是这样。
x509sha1
GODEBUG 设置将在下一个 Go 主要版本(Go 1.24)中移除。这意味着 crypto/x509
将不再支持验证使用基于 SHA-1 签名算法的证书上的签名。
新的 ParseOID
函数解析点编码的 ASN.1 Object Identifier 字符串。OID
类型现在实现了 encoding.BinaryMarshaler
、encoding.BinaryUnmarshaler
、encoding.TextMarshaler
、encoding.TextUnmarshaler
接口。
database/sql
由 driver.Valuer
实现返回的错误现在被包装(wrapped),以改进在诸如 DB.Query
、DB.Exec
和 DB.QueryRow
等操作期间的错误处理。
debug/elf
debug/elf
包现在定义了 PT_OPENBSD_NOBTCFI
。此 ProgType
用于禁用 OpenBSD 二进制文件上的分支跟踪控制流完整性 (BTCFI) 强制执行。
现在定义了符号类型常量 STT_RELC
、STT_SRELC
和 STT_GNU_IFUNC
。
encoding/binary
新的 Encode
和 Decode
函数是 Read
和 Write
的字节切片等价物。Append
允许将多个数据序列化(marshaling)到同一个字节切片中。
go/ast
新的 Preorder
函数返回一个方便的迭代器,用于遍历抽象语法树(syntax tree)的所有节点。
go/types
Func
类型代表一个函数或方法符号,现在有一个 Func.Signature
方法,该方法返回函数的类型,该类型始终是 Signature
。
Alias
类型现在有一个 Rhs
方法,该方法返回其声明右侧的类型:给定 type A = B
,A 的 Rhs
是 B。(#66559)
添加了 Alias.Origin
、Alias.SetTypeParams
、Alias.TypeParams
和 Alias.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
类型允许微调 TCP 连接的 keep-alive 选项,通过新的 TCPConn.SetKeepAliveConfig
方法以及 Dialer
和 ListenConfig
的新 KeepAliveConfig 字段实现。
DNSError
类型现在包装(wraps)由超时或取消导致的错误。例如,errors.Is(someDNSErr, context.DeadlineExceedeed)
现在将报告 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 header 值并返回其中设置的所有 cookie。由于相同的 cookie 名称可以出现多次,返回的 Values 对于给定键可能包含多个值。
新的 ParseSetCookie
函数解析 Set-Cookie header 值并返回一个 cookie。语法错误时返回错误。
ServeContent
、ServeFile
和 ServeFileFS
现在在提供错误时会移除 Cache-Control
、Content-Encoding
、Etag
和 Last-Modified
头。这些头通常适用于非错误内容,但不适用于错误文本。
包装(wraps) ResponseWriter
并应用即时编码(例如 Content-Encoding: gzip
)的中间件在此更改后将无法正常工作。ServeContent
、ServeFile
和 ServeFileFS
的先前行为可以通过设置 GODEBUG=httpservecontentkeepheaders=1
恢复。
请注意,当 ServeContent
处理 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 时,错误地返回了 true,尽管使用 ==
或 Addr.Compare
比较时 Addr 值是不同的。此错误现已修复,所有三种方法现在报告相同的结果。
os
Stat
函数现在为 Windows 上是 Unix socket 的文件设置 ModeSocket
位。这些文件通过设置了 IO_REPARSE_TAG_AF_UNIX
的重解析点标签来标识。
在 Windows 上,Lstat
和 Stat
报告的重解析点的模式位发生了变化。挂载点不再设置 ModeSymlink
,而那些非符号链接、非 Unix socket 或非重复删除文件的重解析点现在始终设置 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.Pointer
和 Value.UnsafePointer
方法现在支持 String
类型的反射值。
新的 Value.Seq
和 Value.Seq2
方法返回序列,这些序列以如同在 for/range 循环中使用的方式遍历值。新的 Type.CanSeq
和 Type.CanSeq2
方法报告分别调用 Value.Seq
和 Value.Seq2
是否会成功而不发生 panic。
runtime/debug
SetCrashOutput
函数允许用户指定一个备用文件,运行时应将致命崩溃报告写入该文件。它可用于构建所有意外崩溃的自动化报告机制,而不仅仅是那些显式使用 recover
的 goroutine 中的崩溃。
runtime/pprof
alloc
、mutex
、block
、threadcreate
和 goroutine
配置的最大堆栈深度已从 32 帧提高到 128 帧。
runtime/trace
运行时现在在程序因未捕获的 panic 而崩溃时会显式刷新跟踪数据。这意味着如果程序在跟踪活跃时崩溃,跟踪中将提供更完整的跟踪数据。
slices
Repeat
函数返回一个新切片,该切片将提供的切片重复给定次数。
sync
Map.Clear
方法删除所有条目,得到一个空的 Map
。它类似于 clear
。
sync/atomic
新的 And
和 Or
操作符对给定输入应用按位 AND
或 OR
运算,并返回旧值。
syscall
syscall 包现在在 Windows 上定义了 WSAENOPROTOOPT
。
GetsockoptInt
函数现在在 Windows 上受支持。
testing/fstest
TestFS
现在返回一个结构化错误,该错误可以被解包(通过方法 Unwrap() []error
)。这允许使用 errors.Is
或 errors.As
检查错误。
text/template
模板现在支持新的 “else with” 动作,这在某些使用场景下降低了模板的复杂性。
time
Parse
和 ParseInLocation
现在在时区偏移超出范围时返回错误。
在 Windows 上,Timer
、Ticker
以及使 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 用户模式应用 profile。允许的值为 rva20u64
和 rva22u64
。
GORISCV64
环境变量默认为 rva20u64
。
Wasm
GOROOT/misc/wasm
中的 go_wasip1_wasm_exec
脚本已停止支持版本低于 14.0.0 的 wasmtime
。