go1.23 新特性

go1.23 新特性

这里面主要记一些语法相关的变化,或者说是我更关注的地方的变化。

Linker

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

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

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

简单来说就是说不允许直接 linkname 一些标准库符号,从下面的示例来看一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
_ "strings"
_ "unsafe"
)

//go:linkname explode strings.explode
func explode(s string, n int) []string

func main() {
fmt.Println(explode("你好go语言", -1))
}

go 1.13 之前代码是可以正常输出

1
2
$ go run .
[你 好 g o 语 言]

尝试使用 go1.23 运行

1
2
3
$ go run .
# linkname
link: main: invalid reference to strings.explode

可以看出程序已经无法正常运行

使用 -checklinkname=0 禁用此检查。

1
2
3
$ go run -ldflags=-checklinkname=0 .
[你 好 g o 语 言]

至于禁用的原因是 “这种使用内部类型的情况是不可持续的”,是的一般这样就是要link一些内部的私有符号,而标准库将这些符号声明为私有的就是为了避免随意的访问,另外大量的访问就会使得私有符号有了公共符号的作用,使得内部的变更变得困难。

range-over-func

Go 1.23 将 (Go 1.22) 的“range-over-func”实验正式纳入语言中。现在,“for-range”循环中的“range”子句可以接受以下类型的迭代器函数作为 range 表达式:

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

这些类型的函数调用迭代器参数函数来生成“for-range”循环的迭代值。有关详细信息,请参阅 iter 包文档和语言规范。关于动机,参见 2022 年的“range-over-func”讨论。

这块主要是看下 这个 range-over-func 如何使用吧

先看个示例代码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"fmt"
"slices"
)

func Backward[E any](s []E) func(func(int, E) bool) {
return func(yield func(int, E) bool) {
for i := len(s) - 1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}

func main() {
s := []string{"hello", "world"}
for i, x := range slices.Backward(s) {
fmt.Println(i, x)
}

// fibo generates the Fibonacci sequence
fibo := func(yield func(x int) bool) {
f0, f1 := 0, 1
for yield(f0) {
f0, f1 = f1, f0+f1
}
}

// print the Fibonacci numbers below 1000:
for x := range fibo {
if x >= 1000 {
break
}
fmt.Printf("%d ", x)
}
}

输出

1
2
3
4
$ go run .
1 world
0 hello
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

理解有的晦涩,我写一下我对第二个循环的理解吧

1
2
3
4
5
6
7
for {
if f0 >= 1000 {
break
}
fmt.Printf("%d ", f0)
f0, f1 = f1, f0+f1
}

有的像其他语言的 forEach 循环, 首先 yield 的入参就是 range 返回的内容,而 for 大括号内的内容就是 yield 的方法体,continue 会变成 return true ,而 break 是 return false

其他变更和以上变更详细信息参见Go 1.23 Release Notes