Go 1.24 版本于 2025 年 2 月发布,带来了多项改进和新功能,主要集中在工具链、运行时和标准库的实现上。此版本保持了 Go 1 的兼容性承诺,确保绝大多数 Go 程序能够继续编译和运行。
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] 是等价的。
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 构建缓存中。go build 和 go install 命令现在接受 -json 标志,以 JSON 格式报告构建输出和失败。go test -json 现在也以 JSON 格式报告构建输出和失败。GOAUTH 环境变量提供了一种灵活的身份验证私有模块获取方式。go build 命令现在根据版本控制系统标签和/或提交在编译的二进制文件中设置主模块的版本。可以使用 -buildvcs=false 标志省略二进制文件中的版本控制信息。GODEBUG 设置 toolchaintrace=1 可用于跟踪 go 命令的工具链选择过程。Cgo 支持 C 函数的新注释以提高运行时性能。#cgo noescape cFunctionName 告诉编译器传递给 C 函数 cFunctionName 的内存不会逃逸。#cgo nocallback cFunctionName 告诉编译器 C 函数 cFunctionName 不会回调任何 Go 函数。
objdump 工具现在支持在 64 位 LoongArch (GOARCH=loong64 )、RISC-V (GOARCH=riscv64 ) 和 S390X ( GOARCH=s390x ) 上进行反汇编。
新的 tests 分析器报告测试包中测试、fuzzers、基准测试和示例声明中的常见错误。
运行时的一些性能改进平均降低了 2-3% 的 CPU 开销。这些改进包括一个新的基于 Swiss Tables 的 map 实现、更高效的小对象内存分配和一个新的运行时内部互斥锁实现。
Go 1.24 引入了一种新的内置 map 实现,基于 Swiss Tables。
Swiss Tables 是一种优化的哈希表实现,旨在提高查找和插入操作的性能。它通过使用密集 packed 的元数据数组来存储有关表中条目的存在信息,从而实现优化。这种存在信息允许 Swiss Tables 优化查找和插入操作。每个元数据条目需要一个字节的开销。
Swiss Tables 将哈希函数的结果拆分为两个部分:
元数据存储存在信息(元素是空、已删除还是已满)。每个元数据条目由一个字节组成,其中包含一个控制位和 7 位 H2 哈希。
在表中搜索项目时,Swiss Tables 使用 SSE 指令扫描候选匹配项。查找元素的过程可以概括为以下步骤:
新的 map 实现提高了性能,尤其是在并发场景下。
新的 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
}
}
基准测试现在可以使用更快且更不易出错的 testing.B.Loop 方法来执行基准测试迭代。
testing.B.Loop 方法Loop 方法简化了基准测试的结构,并确保基准测试函数只执行一次设置和清理步骤。它还防止编译器完全优化掉循环体。
示例:
func Benchmark(b *testing.B) {
// ... setup ...
for b.Loop() {
// ... code to measure ...
}
// ... cleanup ...
}
新的 runtime.AddCleanup 函数是一种比 runtime.SetFinalizer 更灵活、更高效且更不易出错的终结机制。
新的 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("对象已被垃圾回收") // 输出:对象已被垃圾回收
}
}
新增了 crypto/hkdf、crypto/pbkdf2 和 crypto/sha3 包。
此版本包含一组新的机制,以促进 FIPS 140-3 合规性。
新的实验性 testing/synctest 包提供了对测试并发代码的支持。
testing/synctest 包synctest.Run 函数在一个隔离的“bubble”中启动一组 goroutine。在 bubble 中,time 包函数在假时钟上运行。synctest.Wait 函数等待当前 bubble 中的所有 goroutine 阻塞。
archive/zip 和 archive/tar 中的 (*Writer).AddFS 实现现在为空目录写入目录头。bytes 包添加了多个使用迭代器的函数:Lines:返回一个在字节切片中以换行符结尾的行上的迭代器。SplitSeq:返回一个在分隔符周围分割的字节切片的所有子切片上的迭代器。SplitAfterSeq:返回一个在分隔符的每个实例之后分割的字节切片的子切片上的迭代器。FieldsSeq:返回一个在空格字符运行周围分割的字节切片的子切片上的迭代器。FieldsFuncSeq:返回一个在满足谓词的 Unicode 代码点运行周围分割的字节切片的子切片上的迭代器。NewCipher 返回的值不再实现 NewCTR、NewGCM、NewCBCEncrypter 和 NewCBCDecrypter 方法。NewGCMWithRandomNonce 函数返回一个通过在 Seal 期间生成随机 nonce 并将其前置到密文来实现 AES-GCM 的 AEAD。NewOFB, NewCFBEncrypter, 和 NewCFBDecrypter 现在已弃用。PrivateKey.Sign 现在根据 RFC 6979 生成确定性签名。md5.New 返回的值现在也实现了 encoding.BinaryAppender 接口。Read 函数现在保证不会失败。新的 Text 函数可用于生成加密安全的随机文本字符串。GenerateKey 现在返回一个错误。sha1.New 返回的值现在也实现了 encoding.BinaryAppender 接口。sha256.New 和 sha256.New224 返回的值现在也实现了 encoding.BinaryAppender 接口。sha512.New, sha512.New384, sha512.New512_224 和 sha512.New512_256 返回的值现在也实现了 encoding.BinaryAppender 接口。WithDataIndependentTiming 函数允许用户运行一个启用了架构特定功能的函数,这些功能保证特定指令是数据值时间不变的。x509sha1 GODEBUG 设置已删除。Certificate.Verify 不再支持基于 SHA-1 的签名。debug/elf 包添加了对处理动态 ELF (Executable and Linkable Format) 文件中符号版本的支持。TextAppender 和 BinaryAppender,用于将对象的文本或二进制表示形式附加到字节切片。omitzero 选项的 struct 字段将被省略。go/types 数据结构现在也具有返回迭代器的方法。New 返回的值现在也实现了 encoding.BinaryAppender 接口。New 和 NewIEEE 返回的值现在也实现了 encoding.BinaryAppender 接口。New 返回的值现在也实现了 encoding.BinaryAppender 接口。New32, New32a, New64, New64a, New128 和 New128a 返回的值现在也实现了 encoding.BinaryAppender 接口。Comparable 和 WriteComparable 函数可以计算任何可比较值的哈希值。DiscardHandler 是一个从不启用并且总是丢弃其输出的处理程序。Float, Int 和 Rat 现在实现了 encoding.TextAppender 接口。Seed 函数不再有任何效果。ChaCha8 和 PCG 现在实现了 encoding.BinaryAppender 接口。ListenConfig 现在默认在支持它的系统上使用 MPTCP (目前仅在 Linux 上)。Transport 对响应请求收到的 1xx 信息响应的限制已更改。Addr, AddrPort 和 Prefix 现在实现了 encoding.BinaryAppender 和 encoding.TextAppender 接口。URL 现在也实现了 encoding.BinaryAppender 接口。Current 现在可以在 Windows Nano Server 中使用。Regexp 现在实现了 encoding.TextAppender 接口。GOROOT 函数现在已弃用。strings 包添加了多个使用迭代器的函数 (同 bytes 包)。sync.Map 的实现已更改,提高了性能,尤其是在 map 修改方面。T.Context 和 B.Context 方法返回一个在测试完成后并在测试清理函数运行之前取消的上下文。新的 T.Chdir 和 B.Chdir 方法可用于更改测试或基准测试期间的工作目录。Time 现在实现了 encoding.BinaryAppender 和 encoding.TextAppender 接口。encoding/json, strings, bytes 的变化encoding/json:omitzero 选项:用于在 JSON 编组时省略零值字段。UnmarshalTypeError.Field:现在包括嵌入式结构,以提供更详细的错误消息。strings 和 bytes:Lines、SplitSeq、SplitAfterSeq、FieldsSeq 和 FieldsFuncSeq。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 成为一种更强大、更灵活和更高效的编程语言。你升级了吗?