Go 1.24 发布说明

Go 1.24 简介

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

语言变化

Go 1.24 现在完全支持泛型类型别名:类型别名可以像定义类型一样参数化。详情请参阅语言规范。目前,可以通过设置 GOEXPERIMENT=noaliastypeparams 来禁用此特性;但 aliastypeparams 设置将在 Go 1.25 中移除。

工具

Go 命令

Go modules 现在可以使用 go.mod 中的 tool 指令来跟踪可执行依赖。这消除了之前将工具作为空白导入添加到通常命名为“tools.go”文件中的变通方法。现在,go tool 命令除了 Go 分发版附带的工具外,还可以运行这些工具。更多信息请参阅文档

go get 的新 -tool 标志除了添加 require 指令外,还会在当前模块中为指定包添加 tool 指令。

新的tool 元模式指代当前模块中的所有工具。可以使用 go get tool 来升级所有工具,或使用 go install tool 将它们安装到你的 GOBIN 目录中。

go rungo tool 的新行为创建的可执行文件现在缓存在 Go 构建缓存中。这使得重复执行更快,但会增大缓存。参见#69290

go buildgo install 命令现在接受一个 -json 标志,该标志将构建输出和失败以结构化 JSON 格式报告到标准输出。有关报告格式的详细信息,请参见 go help buildjson

此外,go test -json 现在以 JSON 格式报告构建输出和失败,与测试结果 JSON 交错。这些通过新的 Action 类型区分,但如果它们在测试集成系统中引起问题,可以使用GODEBUG 设置 gotestjsonbuildtext=1 恢复到文本构建输出。

新的 GOAUTH 环境变量提供了一种灵活的方式来认证私有模块的获取。更多信息请参见 go help goauth

go build 命令现在根据版本控制系统标签和/或提交在编译后的二进制文件中设置主模块的版本。如果存在未提交的更改,将附加 +dirty 后缀。使用 -buildvcs=false 标志可以从二进制文件中省略版本控制信息。

新的GODEBUG 设置 toolchaintrace=1 可用于跟踪 go 命令的工具链选择过程。

Cgo

Cgo 支持 C 函数的新注解以提高运行时性能。#cgo noescape cFunctionName 告诉编译器传递给 C 函数 cFunctionname 的内存不会逃逸。#cgo nocallback cFunctionName 告诉编译器 C 函数 cFunctionName 不会回调任何 Go 函数。更多信息请参见cgo 文档

Cgo 当前拒绝编译具有多个不兼容声明的 C 函数调用。例如,如果 f 同时声明为 void f(int)void f(double),cgo 将报告错误,而不是可能为 f(0) 生成错误的调用序列。此版本的新功能是当不兼容声明出现在不同文件中时,对此错误条件的检测器更好。参见#67699

Objdump

objdump 工具现在支持在 64 位 LoongArch (GOARCH=loong64)、RISC-V (GOARCH=riscv64) 和 S390X (GOARCH=s390x) 上进行反汇编。

Vet

新的 tests 分析器报告测试包中测试、模糊测试、基准测试和示例声明中的常见错误,例如格式错误的名称、不正确的签名或文档中引用的标识符不存在的示例。其中一些错误可能导致测试无法运行。此分析器属于 go test 运行的分析器子集之一。

现有的 printf 分析器现在会报告形式为 fmt.Printf(s) 的调用诊断,其中 s 是非常量格式字符串且没有其他参数。此类调用几乎总是一个错误,因为 s 的值可能包含 % 符号;应使用 fmt.Print 代替。参见#60529。此检查往往会在现有代码中发现问题,因此仅在语言版本(由 go.mod go 指令或 //go:build 注释指定)至少为 Go 1.24 时应用,以避免在更新到 1.24 Go 工具链时导致持续集成失败。

现有的 buildtag 分析器现在会在 //go:build 指令中存在无效的 Go 主版本构建约束时报告诊断。例如,//go:build go1.23.1 指的是次要版本;应使用 //go:build go1.23 代替。参见#64127

现有的 copylock 分析器现在会在 3 段式“for”循环(例如 for i := iter(); done(i); i = next(i) { ... })中声明的变量包含 sync.Locker(例如 sync.Mutex)时报告诊断。Go 1.22 更改了这些循环的行为,为每次迭代创建一个新变量,并复制前一次迭代的值;此复制操作对于锁而言是不安全的。参见#66387

GOCACHEPROG

cmd/go 内部的二进制和测试缓存机制现在可以由实现 cmd/go 工具与 GOCACHEPROG 环境变量指定的子进程之间 JSON 协议的子进程来实现。这之前是一个 GOEXPERIMENT 特性。有关协议详情,请参见文档

运行时

运行时的一些性能改进使 CPU 开销在代表性基准测试套件上平均降低了 2-3%。结果可能因应用程序而异。这些改进包括基于 Swiss Tables 的新内置 map 实现,更高效的小对象内存分配,以及新的运行时内部互斥锁实现。

新的内置 map 实现和新的运行时内部互斥锁可以在构建时分别通过设置 GOEXPERIMENT=noswissmapGOEXPERIMENT=nospinbitmutex 来禁用。

编译器

编译器之前已经禁止为 cgo 生成的接收者类型定义新方法,但可以通过别名类型绕过该限制。Go 1.24 现在始终会报告错误,如果接收者直接或间接(通过别名类型)表示 cgo 生成的类型。

链接器

链接器现在默认在 ELF 平台生成 GNU 构建 ID(ELF NT_GNU_BUILD_ID note),并在 macOS 上生成 UUID(Mach-O LC_UUID load command)。构建 ID 或 UUID 源自 Go 构建 ID。可以使用 -B none 链接器标志禁用,或使用 -B 0xNNNN 链接器标志并指定用户十六进制值来覆盖。

自举

Go 1.22 发布说明所述,Go 1.24 现在需要 Go 1.22.6 或更高版本才能进行自举。我们预计 Go 1.26 将需要 Go 1.24 或更高版本的次要版本进行自举。

标准库

目录限制的文件系统访问

新的 os.Root 类型提供了在特定目录内执行文件系统操作的能力。

os.OpenRoot 函数打开一个目录并返回一个 os.Rootos.Root 上的方法在目录内操作,并且不允许引用目录外部位置的路径,包括那些跟随符号链接出目录的路径。os.Root 上的方法镜像了 os 包中大多数文件系统操作,例如 os.Root.Openos.Root.Createos.Root.Mkdiros.Root.Stat

新的基准测试函数

基准测试现在可以使用更快且不易出错的 testing.B.Loop 方法来执行基准测试迭代,例如 for b.Loop() { ... },取代通常涉及 b.N 的循环结构,例如 for range b.N。这提供了两个显著的优势

改进的终结器

新的 runtime.AddCleanup 函数是一种终结机制,它比 runtime.SetFinalizer 更灵活、更高效且不易出错。AddCleanup 将一个清理函数附加到对象上,当该对象不再可达时,清理函数将运行。然而,与 SetFinalizer 不同,单个对象可以附加多个清理函数,清理函数可以附加到内部指针,当对象形成循环时,清理函数通常不会导致内存泄漏,并且清理函数不会延迟释放对象或其指向的对象。新代码应优先使用 AddCleanup 而非 SetFinalizer

新的 weak 包

新的 weak 包提供了弱指针。

弱指针是一种低级原语,用于创建内存高效的结构,例如用于关联值的弱映射、用于包 unique 未涵盖的任何内容的规范化映射,以及各种类型的缓存。为了支持这些用例,此版本还提供了 runtime.AddCleanupmaphash.Comparable

新的 crypto/mlkem 包

新的 crypto/mlkem 包实现了 ML-KEM-768 和 ML-KEM-1024。

ML-KEM 是一种后量子密钥交换机制,之前称为 Kyber,并在 FIPS 203 中指定。

新的 crypto/hkdf、crypto/pbkdf2 和 crypto/sha3 包

新的 crypto/hkdf 包实现了基于 HMAC 的提取-扩展密钥派生函数 HKDF,定义于 RFC 5869

新的 crypto/pbkdf2 包实现了基于密码的密钥派生函数 PBKDF2,定义于 RFC 8018

新的 crypto/sha3 包实现了 SHA-3 哈希函数以及 SHAKE 和 cSHAKE 可扩展输出函数,定义于 FIPS 202

这三个包都基于现有的 golang.org/x/crypto/... 包。

符合 FIPS 140-3

此版本包含一套新的机制以促进符合 FIPS 140-3

Go 加密模块是一组内部标准库包,用于透明地实现 FIPS 140-3 批准的算法。应用程序无需更改即可使用 Go 加密模块来实现批准的算法。

新的 GOFIPS140 环境变量可用于选择构建中使用的 Go 加密模块版本。新的 fips140 GODEBUG 设置 可用于在运行时启用 FIPS 140-3 模式。

Go 1.24 包含 Go 加密模块版本 v1.0.0,该版本目前正在 CMVP 认可的实验室进行测试。

新的实验性 testing/synctest 包

新的实验性 testing/synctest 包提供了对测试并发代码的支持。

更多详情请参阅包文档。

synctest 包是实验性的,必须在构建时通过设置 GOEXPERIMENT=synctest 来启用。此包的 API 在未来版本中可能会发生变化。有关更多信息并提供反馈,请参阅问题 #67434

库的次要更改

archive

archive/ziparchive/tar 中的 (*Writer).AddFS 实现现在会为包含空目录写入目录头。

bytes

bytes 包添加了几个使用迭代器工作的函数

crypto/aes

NewCipher 返回的值不再实现 NewCTRNewGCMNewCBCEncrypterNewCBCDecrypter 方法。这些方法没有文档记录,并且并非在所有架构上都可用。相反,Block 值应直接传递给相关的 crypto/cipher 函数。目前,即使标准库不再使用这些方法,crypto/cipher 仍然会检查 Block 值上是否存在这些方法。

crypto/cipher

新的 NewGCMWithRandomNonce 函数返回一个 AEAD,它通过在 Seal 期间生成随机 nonce 并将其添加到密文前缀来实现 AES-GCM。

当与 crypto/aes 一起使用时,由 NewCTR 返回的 Stream 实现在 amd64 和 arm64 上现在快了几倍。

NewOFBNewCFBEncrypterNewCFBDecrypter 现在已弃用。OFB 和 CFB 模式未经过认证,这通常会使主动攻击者能够操纵和恢复明文。建议应用程序改用 AEAD 模式。如果需要未经认证的 Stream 模式,请改用 NewCTR

crypto/ecdsa

如果随机源为 nil,PrivateKey.Sign 现在根据 RFC 6979 生成确定性签名。

crypto/md5

md5.New 返回的值现在也实现了 encoding.BinaryAppender 接口。

crypto/rand

Read 函数现在保证不会失败。它将始终返回 nil 作为 error 结果。如果 Read 在从 Reader 读取时遇到错误,程序将不可恢复地崩溃。请注意,默认 Reader 使用的平台 API 文档说明它们总是成功,因此此更改应该只影响覆盖 Reader 变量的程序。一个例外是 3.17 版本之前的 Linux 内核,其中默认的 Reader 仍然打开 /dev/urandom 并且可能失败。

在 Linux 6.11 及更高版本上,Reader 现在通过 vDSO 使用 getrandom 系统调用。这速度快了几倍,特别是对于小量读取。

在 OpenBSD 上,Reader 现在使用 arc4random_buf(3)

新的 Text 函数可用于生成加密安全的随机文本字符串。

crypto/rsa

GenerateKey 现在在请求小于 1024 位密钥时返回错误。所有 Sign、Verify、Encrypt 和 Decrypt 方法在使用小于 1024 位密钥时现在都会返回错误。此类密钥不安全,不应使用。GODEBUG 设置 rsa1024min=0 可以恢复旧的行为,但我们建议仅在必要时并在测试中使用,例如在测试文件中添加 //go:debug rsa1024min=0 行。新的 GenerateKey 示例 提供了一个易于使用的标准 2048 位测试密钥。

现在在调用 PrivateKey.Validate 之前调用 PrivateKey.Precompute 是安全且更高效的。在存在部分填充的 PrecomputedValues 时(例如从 JSON 反序列化密钥时),Precompute 现在更快。

现在,即使未调用 Validate,该包也会拒绝更多无效密钥,并且 GenerateKey 对于损坏的随机源可能会返回新的错误。PrivateKeyPrimesPrecomputed 字段现在即使缺少某些值也会被使用和验证。另请参见下文描述crypto/x509 对 RSA 密钥解析和序列化的更改。

SignPKCS1v15VerifyPKCS1v15 现在支持 SHA-512/224、SHA-512/256 和 SHA-3。

GenerateKey 现在使用稍微不同的方法生成私有指数(Carmichael 的 totient 而非 Euler 的 totient)。极少数仅从素因数外部重新生成密钥的应用程序可能会产生不同但兼容的结果。

在 wasm 上,公钥和私钥操作现在快了两倍。

crypto/sha1

sha1.New 返回的值现在也实现了 encoding.BinaryAppender 接口。

crypto/sha256

sha256.Newsha256.New224 返回的值现在也实现了 encoding.BinaryAppender 接口。

crypto/sha512

sha512.Newsha512.New384sha512.New512_224sha512.New512_256 返回的值现在也实现了 encoding.BinaryAppender 接口。

crypto/subtle

新的 WithDataIndependentTiming 函数允许用户启用特定架构的特性来运行函数,这些特性保证特定指令在数据值计时上是不变的。这可以用于确保设计为恒定时间运行的代码不会被 CPU 级别的特性优化而导致在可变时间内运行。目前,WithDataIndependentTiming 在 arm64 上使用 PSTATE.DIT 位,并在所有其他架构上是一个无操作。GODEBUG 设置 dataindependenttiming=1 为整个 Go 程序启用 DIT 模式。

XORBytes 的输出必须与输入完全重叠或完全不重叠。之前,其行为未定义,现在 XORBytes 将会 panic。

crypto/tls

TLS 服务器现在支持 Encrypted Client Hello (ECH)。通过填充 Config.EncryptedClientHelloKeys 字段可以启用此特性。

新的后量子 X25519MLKEM768 密钥交换机制现在受到支持,并且当 Config.CurvePreferences 为 nil 时默认启用。GODEBUG 设置 tlsmlkem=0 可以恢复默认行为。这在处理无法正确处理大记录从而导致握手期间超时的有 bug 的 TLS 服务器时可能有用(参见 TLS post-quantum TL;DR fail)。

已移除对实验性 X25519Kyber768Draft00 密钥交换的支持。

密钥交换排序现在完全由 crypto/tls 包处理。Config.CurvePreferences 的顺序现在被忽略,其内容仅用于确定在填充该字段时启用哪些密钥交换。

新的 ClientHelloInfo.Extensions 字段列出了在 Client Hello 消息中接收到的扩展的 ID。这对于指纹识别 TLS 客户端可能有用。

crypto/x509

x509sha1 GODEBUG 设置 已被移除。Certificate.Verify 不再支持基于 SHA-1 的签名。

OID 现在实现了 encoding.BinaryAppenderencoding.TextAppender 接口。

默认证书策略字段已从 Certificate.PolicyIdentifiers 更改为 Certificate.Policies。解析证书时,两个字段都将填充,但在创建证书时,策略将从 Certificate.Policies 字段获取,而不是从 Certificate.PolicyIdentifiers 字段获取。此更改可以使用 GODEBUG 设置 x509usepolicies=0 恢复。

CreateCertificate 现在在传递一个带有 nil Certificate.SerialNumber 字段的模板时,将使用符合 RFC 5280 的方法生成序列号,而不是失败。

Certificate.Verify 现在支持策略验证,如 RFC 5280 和 RFC 9618 中所定义。新的 VerifyOptions.CertificatePolicies 字段可以设置为一组可接受的策略 OIDs。只有具有有效策略图的证书链才会从 Certificate.Verify 返回。

MarshalPKCS8PrivateKey 现在返回错误,而不是序列化无效的 RSA 密钥。(MarshalPKCS1PrivateKey 没有错误返回值,并且在提供无效密钥时的行为仍然未定义。)

ParsePKCS1PrivateKeyParsePKCS8PrivateKey 现在使用并验证编码的 CRT 值,因此可能会拒绝之前接受的无效 RSA 密钥。使用 GODEBUG 设置 x509rsacrt=0 可以恢复到重新计算 CRT 值。

debug/elf

debug/elf 包增加了对处理动态 ELF (Executable and Linkable Format) 文件中符号版本的支持。新的 File.DynamicVersions 方法返回 ELF 文件中定义的动态版本列表。新的 File.DynamicVersionNeeds 方法返回此 ELF 文件所需的、在其他 ELF 对象中定义的动态版本列表。最后,新的 Symbol.HasVersionSymbol.VersionIndex 字段指示符号的版本。

encoding

引入了两个新接口,TextAppenderBinaryAppender,用于将对象的文本或二进制表示附加到字节切片。这些接口提供了与 TextMarshalerBinaryMarshaler 相同的功能,但它们直接将数据附加到现有切片,而不是每次都分配一个新切片。标准库中已实现 TextMarshaler 和/或 BinaryMarshaler 的类型现在也实现了这些接口。

encoding/json

序列化时,结构体字段标签中带有新选项 omitzero 的字段,如果其值为零,将被省略。如果字段类型有 IsZero() bool 方法,将使用该方法来确定值是否为零。否则,如果值是其类型的零值,则值为零。当目的是省略零值时,omitzero 字段标签比 omitempty 更清晰且不易出错。特别是,与 omitempty 不同,omitzero 会省略零值的 time.Time 值,这是一个常见的痛点。

如果同时指定了 omitemptyomitzero,则当值为空或零(或两者都是)时,该字段将被省略。

UnmarshalTypeError.Field 现在包含嵌入结构体,以提供更详细的错误消息。

go/types

所有使用一对方法(例如 Len() intAt(int) T)暴露序列的 go/types 数据结构现在也具有返回迭代器的方法,使您可以简化如下代码

params := fn.Type.(*types.Signature).Params()
for i := 0; i < params.Len(); i++ {
   use(params.At(i))
}

为这样

for param := range fn.Signature().Params().Variables() {
   use(param)
}

这些方法包括:Interface.EmbeddedTypes, Interface.ExplicitMethods, Interface.Methods, MethodSet.Methods, Named.Methods, Scope.Children, Struct.Fields, Tuple.Variables, TypeList.Types, TypeParamList.TypeParams, Union.Terms

hash/adler32

New 返回的值现在也实现了 encoding.BinaryAppender 接口。

hash/crc32

NewNewIEEE 返回的值现在也实现了 encoding.BinaryAppender 接口。

hash/crc64

New 返回的值现在也实现了 encoding.BinaryAppender 接口。

hash/fnv

New32New32aNew64New64aNew128New128a 返回的值现在也实现了 encoding.BinaryAppender 接口。

hash/maphash

新的 ComparableWriteComparable 函数可以计算任何可比较值的哈希值。这使得对任何可以用作 Go map 键的值进行哈希成为可能。

log/slog

新的 DiscardHandler 是一个永远不会被启用并总是丢弃其输出的处理器。

LevelLevelVar 现在实现了 encoding.TextAppender 接口。

math/big

FloatIntRat 现在实现了 encoding.TextAppender 接口。

math/rand

对已弃用的顶层 Seed 函数的调用不再有任何效果。要恢复旧的行为,请使用 GODEBUG 设置 randseednop=0。更多背景信息请参阅 提案 #67273

math/rand/v2

ChaCha8PCG 现在实现了 encoding.BinaryAppender 接口。

net

ListenConfig 现在默认在支持 MPTCP 的系统上使用它(目前仅限 Linux)。

IP 现在实现了 encoding.TextAppender 接口。

net/http

Transport 对作为请求响应接收到的 1xx 信息响应的限制已发生变化。它之前在收到超过 5 个 1xx 响应后会中止请求并返回错误。现在,如果所有 1xx 响应的总大小超过 Transport.MaxResponseHeaderBytes 配置设置,它会返回错误。

此外,当请求具有 net/http/httptrace.ClientTrace.Got1xxResponse 跟踪钩子时,现在对 1xx 响应的总数没有限制。Got1xxResponse 钩子可以返回错误以中止请求。

TransportServer 现在具有 HTTP2 字段,允许配置 HTTP/2 协议设置。

新的 Server.ProtocolsTransport.Protocols 字段提供了一种简单的方法来配置服务器或客户端使用的 HTTP 协议。

服务器和客户端可以配置为支持未加密的 HTTP/2 连接。

Server.Protocols 包含 UnencryptedHTTP2 时,服务器将在未加密的端口上接受 HTTP/2 连接。服务器可以在同一端口上接受 HTTP/1 和未加密的 HTTP/2。

Transport.Protocols 包含 UnencryptedHTTP2 且不包含 HTTP1 时,传输将对 http:// URL 使用未加密的 HTTP/2。如果传输配置为同时使用 HTTP/1 和未加密的 HTTP/2,它将使用 HTTP/1。

未加密的 HTTP/2 支持使用“事先知道的 HTTP/2”(RFC 9113,第 3.3 节)。不支持已弃用的“Upgrade: h2c”头部。

net/netip

AddrAddrPortPrefix 现在实现了 encoding.BinaryAppenderencoding.TextAppender 接口。

net/url

URL 现在也实现了 encoding.BinaryAppender 接口。

os/user

在 Windows 上,Current 现在可以在 Windows Nano Server 中使用。实现已更新,以避免使用 NetApi32 库中的函数,该库在 Nano Server 中不可用。

在 Windows 上,CurrentLookupLookupId 现在支持以下内置服务用户帐户:

在 Windows 上,当当前用户加入了一个慢速域(这对于许多公司用户来说是常见情况)时,Current 的速度有了显著提升。新的实现性能现在在毫秒级别,而之前的实现可能需要几秒甚至几分钟才能完成。

在 Windows 上,当当前线程正在模拟另一个用户时,Current 现在返回进程所有者的用户。之前它会返回错误。

regexp

Regexp 现在实现了 encoding.TextAppender 接口。

runtime

GOROOT 函数现已弃用。在新代码中,推荐使用系统路径查找“go”二进制文件,并使用 go env GOROOT 来查找其 GOROOT。

strings

strings 包添加了几个与迭代器配合使用的函数:

sync

sync.Map 的实现已更改,提高了性能,尤其是在 map 修改方面。例如,对不相交键集的修改在大 map 上不太可能发生竞争,并且从 map 中获取低竞争负载不再需要任何预热时间。

如果您遇到任何问题,请在构建时设置 GOEXPERIMENT=nosynchashtriemap 以切换回旧的实现,并请提交问题

testing

新的 T.ContextB.Context 方法返回一个上下文,该上下文在测试完成后且在测试清理函数运行之前被取消。

新的 T.ChdirB.Chdir 方法可用于在测试或基准测试期间更改工作目录。

text/template

模板现在支持 range-over-func 和 range-over-int。

time

Time 现在实现了 encoding.BinaryAppenderencoding.TextAppender 接口。

移植

Linux

正如 Go 1.23 版本说明中宣布的,Go 1.24 需要 Linux 内核版本 3.2 或更高版本。

Darwin

Go 1.24 是最后一个可以在 macOS 11 Big Sur 上运行的版本。Go 1.25 将需要 macOS 12 Monterey 或更高版本。

WebAssembly

添加了 go:wasmexport 编译器指令,用于 Go 程序将函数导出到 WebAssembly 主机。

在 WebAssembly System Interface Preview 1 (GOOS=wasip1 GOARCH=wasm) 上,Go 1.24 支持通过指定 -buildmode=c-shared 构建标志,将 Go 程序构建为 reactor/library

现在允许更多类型作为 go:wasmimport 函数的参数或结果类型。具体来说,允许使用 boolstringuintptr 和某些类型的指针(详细信息请参阅文档),以及已允许的 32 位和 64 位整数和浮点类型以及 unsafe.Pointer。这些类型也允许作为 go:wasmexport 函数的参数或结果类型。

WebAssembly 的支持文件已从 misc/wasm 移至 lib/wasm

初始内存大小显著减小,尤其是对于小型 WebAssembly 应用程序。

Windows

32 位 windows/arm 移植 (GOOS=windows GOARCH=arm) 已被标记为损坏。详细信息请参阅问题 #70705