Go 1.24 版本于 2025 年 2 月发布,带来了多项改进和新功能,主要集中在工具链、运行时和标准库的实现上。此版本保持了 Go 1 的兼容性承诺,确保绝大多数 Go 程序能够继续编译和运行。
语言特性更新
泛型类型别名 (Generic Type Aliases)
Go 1.24 完全支持泛型类型别名。这意味着类型别名可以像定义的类型一样被参数化。
什么是泛型?
泛型允许在编写代码时不指定具体类型,而是在使用时才确定类型。这提高了代码的复用性和灵活性。
泛型用法示例
// 定义一个泛型函数,接受任意类型的切片并返回切片长度
func Length[T any](s []T) int {
return len(s)
}
// 使用泛型函数
sliceInt := []int{1, 2, 3, 4, 5}
sliceString := []string{"a", "b", "c"}
lengthInt := Length(sliceInt) // lengthInt 的值为 5
lengthString := Length(sliceString) // lengthString 的值为 3
本次更新:泛型类型别名
在 Go 1.24 之前,类型别名不能有自己的类型参数。现在,类型别名可以像这样使用:
type Vector[T any] []T
type VectorAlias[T any] = Vector[T] // 泛型类型别名
这意味着 VectorAlias[int]
和 Vector[int]
是等价的。
工具链更新
Go 命令
- Tool 指令:Go 模块现在可以使用
tool
指令在go.mod
文件中跟踪可执行依赖项。这避免了之前将工具作为空白导入添加到名为 “tools.go” 的文件中的变通方法。go tool
命令现在可以运行这些工具,以及 Go 发行版附带的工具。- 使用
go get -tool
命令可以将 tool 指令添加到当前模块。 tool
元模式可以用来升级所有工具go get tool
,或者将它们安装到GOBIN
目录中go install tool
。 示例:go get -tool golang.org/x/tools/cmd/stringer go tool stringer
- 使用
- 缓存:
go run
创建的可执行文件和go tool
的新行为现在缓存在 Go 构建缓存中。 - JSON 输出:
go build
和go install
命令现在接受-json
标志,以 JSON 格式报告构建输出和失败。go test -json
现在也以 JSON 格式报告构建输出和失败。 - GOAUTH 环境变量:新的
GOAUTH
环境变量提供了一种灵活的身份验证私有模块获取方式。 - 版本控制:
go build
命令现在根据版本控制系统标签和/或提交在编译的二进制文件中设置主模块的版本。可以使用-buildvcs=false
标志省略二进制文件中的版本控制信息。 - Toolchain 跟踪:新的
GODEBUG
设置toolchaintrace=1
可用于跟踪go
命令的工具链选择过程。
Cgo
Cgo 支持 C 函数的新注释以提高运行时性能。#cgo noescape cFunctionName
告诉编译器传递给 C 函数 cFunctionName
的内存不会逃逸。#cgo nocallback cFunctionName
告诉编译器 C 函数 cFunctionName
不会回调任何 Go 函数。
Objdump
objdump
工具现在支持在 64 位 LoongArch (GOARCH=loong64 )、RISC-V (GOARCH=riscv64 ) 和 S390X ( GOARCH=s390x ) 上进行反汇编。
Vet
新的 tests
分析器报告测试包中测试、fuzzers、基准测试和示例声明中的常见错误。
运行时更新
运行时的一些性能改进平均降低了 2-3% 的 CPU 开销。这些改进包括一个新的基于 Swiss Tables 的 map
实现、更高效的小对象内存分配和一个新的运行时内部互斥锁实现。
Map Swiss Tables 优化
Go 1.24 引入了一种新的内置 map
实现,基于 Swiss Tables。
什么是 Swiss Tables?
Swiss Tables 是一种优化的哈希表实现,旨在提高查找和插入操作的性能。它通过使用密集 packed 的元数据数组来存储有关表中条目的存在信息,从而实现优化。这种存在信息允许 Swiss Tables 优化查找和插入操作。每个元数据条目需要一个字节的开销。
Swiss Tables 的元数据布局
Swiss Tables 将哈希函数的结果拆分为两个部分:
- H1:57 位哈希值,用于标识元素在表中的索引。
- H2:剩余的 7 位哈希值,用于存储元素的元数据。
Swiss Tables 的元数据信息
元数据存储存在信息(元素是空、已删除还是已满)。每个元数据条目由一个字节组成,其中包含一个控制位和 7 位 H2 哈希。
Swiss Tables 的元数据查找
在表中搜索项目时,Swiss Tables 使用 SSE 指令扫描候选匹配项。查找元素的过程可以概括为以下步骤:
- 使用 H1 哈希查找该哈希的“bucket chain”的开头。
- 使用 H2 哈希构造一个掩码。
- 使用 SSE 指令和掩码生成一组候选匹配项。
- 对每个候选者执行相等性检查。
- 如果在当前候选者中未找到任何元素,则执行探测以生成一组新的候选者。
优化效果
新的 map
实现提高了性能,尤其是在并发场景下。
标准库更新
Directory-limited filesystem access
新的 os.Root
类型提供了在特定目录中执行文件系统操作的能力。
os.Root
类型
os.Root
可以用来限制对单个目录树中文件的访问。Root
上的方法只能访问根目录下的文件和目录。如果传递给 Root
方法的文件名引用了根目录之外的位置,该方法将返回错误。
示例:
package main
import (
"fmt"
"os"
)
func main() {
// 打开根目录
root, err := os.OpenRoot("/tmp")
if err != nil {
fmt.Println(err)
return
}
defer root.Close()
// 在根目录下创建文件
file, err := root.Create("test.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// 尝试访问根目录之外的文件,会返回错误
_, err = root.Stat("/etc/passwd")
if err != nil {
fmt.Println(err) // 输出:stat /etc/passwd: no such file or directory
}
}
New benchmark function
基准测试现在可以使用更快且更不易出错的 testing.B.Loop
方法来执行基准测试迭代。
testing.B.Loop
方法
Loop
方法简化了基准测试的结构,并确保基准测试函数只执行一次设置和清理步骤。它还防止编译器完全优化掉循环体。
示例:
func Benchmark(b *testing.B) {
// ... setup ...
for b.Loop() {
// ... code to measure ...
}
// ... cleanup ...
}
Improved finalizers
新的 runtime.AddCleanup
函数是一种比 runtime.SetFinalizer
更灵活、更高效且更不易出错的终结机制。
New weak package
新的 weak
包提供了弱指针。
弱指针
弱指针是一种低级原语,用于创建内存高效的结构,例如用于关联值的弱映射、用于任何未被唯一包覆盖的规范化映射以及各种类型的缓存。
使用场景:
- 缓存:可以使用弱指针来缓存对象,而不会阻止垃圾回收。
- 规范化映射:可以使用弱指针来创建规范化映射,以确保每个对象只有一个实例。
示例:
package main
import (
"fmt"
"runtime"
"weak"
)
func main() {
// 创建一个对象
obj := new(int)
*obj = 42
// 创建一个弱指针
weakPtr := weak.Make(obj)
// 获取弱指针指向的值
value := weakPtr.Value()
fmt.Println(*value) // 输出:42
// 将对象设置为 nil,使其可以被垃圾回收
obj = nil
runtime.GC()
// 再次获取弱指针指向的值
value = weakPtr.Value()
if value == nil {
fmt.Println("对象已被垃圾回收") // 输出:对象已被垃圾回收
}
}
New crypto/* packages
新增了 crypto/hkdf
、crypto/pbkdf2
和 crypto/sha3
包。
FIPS 140-3 compliance
此版本包含一组新的机制,以促进 FIPS 140-3 合规性。
New experimental testing/synctest package
新的实验性 testing/synctest
包提供了对测试并发代码的支持。
testing/synctest
包
synctest.Run
函数在一个隔离的“bubble”中启动一组 goroutine。在 bubble 中,time
包函数在假时钟上运行。synctest.Wait
函数等待当前 bubble 中的所有 goroutine 阻塞。
其他包的调整
- archive:
archive/zip
和archive/tar
中的(*Writer).AddFS
实现现在为空目录写入目录头。 - bytes:
bytes
包添加了多个使用迭代器的函数:Lines
:返回一个在字节切片中以换行符结尾的行上的迭代器。SplitSeq
:返回一个在分隔符周围分割的字节切片的所有子切片上的迭代器。SplitAfterSeq
:返回一个在分隔符的每个实例之后分割的字节切片的子切片上的迭代器。FieldsSeq
:返回一个在空格字符运行周围分割的字节切片的子切片上的迭代器。FieldsFuncSeq
:返回一个在满足谓词的 Unicode 代码点运行周围分割的字节切片的子切片上的迭代器。
- crypto/aes:
NewCipher
返回的值不再实现NewCTR
、NewGCM
、NewCBCEncrypter
和NewCBCDecrypter
方法。 - crypto/cipher:新的
NewGCMWithRandomNonce
函数返回一个通过在 Seal 期间生成随机 nonce 并将其前置到密文来实现 AES-GCM 的 AEAD。NewOFB
,NewCFBEncrypter
, 和NewCFBDecrypter
现在已弃用。 - crypto/ecdsa:如果随机源为 nil,
PrivateKey.Sign
现在根据 RFC 6979 生成确定性签名。 - crypto/md5:
md5.New
返回的值现在也实现了encoding.BinaryAppender
接口。 - crypto/rand:
Read
函数现在保证不会失败。新的Text
函数可用于生成加密安全的随机文本字符串。 - crypto/rsa:如果请求的密钥小于 1024 位,
GenerateKey
现在返回一个错误。 - crypto/sha1:
sha1.New
返回的值现在也实现了encoding.BinaryAppender
接口。 - crypto/sha256:
sha256.New
和sha256.New224
返回的值现在也实现了encoding.BinaryAppender
接口。 - crypto/sha512:
sha512.New
,sha512.New384
,sha512.New512_224
和sha512.New512_256
返回的值现在也实现了encoding.BinaryAppender
接口。 - crypto/subtle:新的
WithDataIndependentTiming
函数允许用户运行一个启用了架构特定功能的函数,这些功能保证特定指令是数据值时间不变的。 - crypto/tls:TLS 服务器现在支持加密客户端 Hello (ECH)。
- crypto/x509:
x509sha1
GODEBUG 设置已删除。Certificate.Verify
不再支持基于 SHA-1 的签名。 - debug/elf:
debug/elf
包添加了对处理动态 ELF (Executable and Linkable Format) 文件中符号版本的支持。 - encoding:引入了两个新接口
TextAppender
和BinaryAppender
,用于将对象的文本或二进制表示形式附加到字节切片。 - encoding/json:在编组时,如果 struct 字段的值为零,则 struct 字段标签中具有新的
omitzero
选项的 struct 字段将被省略。 - go/types:所有
go/types
数据结构现在也具有返回迭代器的方法。 - hash/adler32:
New
返回的值现在也实现了encoding.BinaryAppender
接口。 - hash/crc32:
New
和NewIEEE
返回的值现在也实现了encoding.BinaryAppender
接口。 - hash/crc64:
New
返回的值现在也实现了encoding.BinaryAppender
接口。 - hash/fnv:
New32
,New32a
,New64
,New64a
,New128
和New128a
返回的值现在也实现了encoding.BinaryAppender
接口。 - hash/maphash:新的
Comparable
和WriteComparable
函数可以计算任何可比较值的哈希值。 - log/slog:新的
DiscardHandler
是一个从不启用并且总是丢弃其输出的处理程序。 - math/big:
Float
,Int
和Rat
现在实现了encoding.TextAppender
接口。 - math/rand:调用已弃用的顶级
Seed
函数不再有任何效果。 - math/rand/v2:
ChaCha8
和PCG
现在实现了encoding.BinaryAppender
接口。 - net:
ListenConfig
现在默认在支持它的系统上使用 MPTCP (目前仅在 Linux 上)。 - net/http:
Transport
对响应请求收到的 1xx 信息响应的限制已更改。 - net/netip:
Addr
,AddrPort
和Prefix
现在实现了encoding.BinaryAppender
和encoding.TextAppender
接口。 - net/url:
URL
现在也实现了encoding.BinaryAppender
接口。 - os/user:在 Windows 上,
Current
现在可以在 Windows Nano Server 中使用。 - regexp:
Regexp
现在实现了encoding.TextAppender
接口。 - runtime:
GOROOT
函数现在已弃用。 - strings:
strings
包添加了多个使用迭代器的函数 (同bytes
包)。 - sync:
sync.Map
的实现已更改,提高了性能,尤其是在 map 修改方面。 - testing:新的
T.Context
和B.Context
方法返回一个在测试完成后并在测试清理函数运行之前取消的上下文。新的T.Chdir
和B.Chdir
方法可用于更改测试或基准测试期间的工作目录。 - text/template:模板现在支持 range-over-func 和 range-over-int。
- time:
Time
现在实现了encoding.BinaryAppender
和encoding.TextAppender
接口。
encoding/json
, strings
, bytes
的变化
encoding/json
:omitzero
选项:用于在 JSON 编组时省略零值字段。UnmarshalTypeError.Field
:现在包括嵌入式结构,以提供更详细的错误消息。
strings
和bytes
:- 新增了多个使用迭代器的函数,例如
Lines
、SplitSeq
、SplitAfterSeq
、FieldsSeq
和FieldsFuncSeq
。
- 新增了多个使用迭代器的函数,例如
Golang 中的迭代器 (Iterators)
Golang 本身并没有像其他一些语言那样提供显式的迭代器接口。然而,Go 1.24 中 strings
和 bytes
包添加的这些新函数,可以看作是一种隐式的迭代器模式的应用。
这些函数返回一个可迭代的对象(通常是一个闭包或函数),每次调用该对象时,它都会返回序列中的下一个值,直到序列结束。
示例:使用 strings.SplitSeq
package main
import (
"fmt"
"strings"
)
func main() {
text := "hello,world,golang"
separator := ","
// SplitSeq 返回一个函数,每次调用都返回下一个子字符串
iterator := strings.SplitSeq(text, separator)
// 循环调用 iterator 直到返回空字符串
for {
substring, ok := iterator()
if !ok {
break // 序列结束
}
fmt.Println(substring)
}
}
这些新函数的作用和用法
Lines
: 用于按行迭代字符串或字节切片。SplitSeq
和SplitAfterSeq
: 用于按指定的分隔符迭代字符串或字节切片。SplitSeq
返回分隔符之间的子字符串,而SplitAfterSeq
返回包含分隔符的子字符串。FieldsSeq
: 用于按空格分割字符串或字节切片,并迭代分割后的字段。FieldsFuncSeq
: 类似于FieldsSeq
,但使用一个函数来判断字段的分割位置。
结语
Go 1.24 发布有些天了,今天详细了解了下,确实带来了许多有用的新功能和改进,包括对泛型类型别名的完全支持、新的工具链功能、运行时性能改进以及标准库的增强功能。这些更改使 Go 成为一种更强大、更灵活和更高效的编程语言。你升级了吗?