Scott

golang1.22新特性 a year ago

go
4552个字符
共有188人围观

前言

自go1.21,历时6个月,golang官方发布了go1.22。

相比之前的版本,这次更新主要体现在工具链,runtime还有标准库

主要更新如下:

从语言层面来看for loop有2大更新

1, go1.22之前,for循环的变量只创建一次,然后在每次迭代的时候来更新此变量,这样就会有个潜在的bug, 来看下面的示例代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	for i := 0; i < 10; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
	time.Sleep(time.Second)
}

在1.22之前运行是下面的结果:

golang1.22后,这个bug就被消除了

2, for range可以用在整型上

package main

import "fmt"

func main() {
	for x := range 10 {
		fmt.Println(x)
	}
}

此外go1.22还可以提前体验range-over-function iterators, 编译的时候需要使用GOEXPERIMENT=rangefunc开启, 格式如下:

GOEXPERIMENT=rangefunc go install my/program
GOEXPERIMENT=rangefunc go build my/program
GOEXPERIMENT=rangefunc go test my/program
GOEXPERIMENT=rangefunc go test my/program -bench=.

请看这个示例:

在goland里是报错的,因为此特性还没“真正”的被引入go1.22

我们可以按照IDE的提示,点击enable然后直接run

我们换个方法,尝试从terminal中运行:

Runtime

一句话总结:提升了CPU的性能,降低了内存使用率, 同时将原来16字节的对齐方式改为了8个字节(如果你学过C++ 的struct,就知道sturct字段的顺序会影响Object的大小,其原因就是字节对齐)

Compiler

使用PGO性能会显著提升

以下是个demo, 运行时go run xxx.go即可

package main

import (
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
}

func main() {
	http.HandleFunc("/", helloHandler)

	fmt.Println("Starting server at port 10097")
	if err := http.ListenAndServe(":10097", nil); err != nil {
		log.Fatal(err)
	}
}

Core library

math/rand/v2

math/rand/v2是Go 1.22第一个v2版本被引入的pkg,相比math/rand的变化在#61716中有详细描述。

最重要的变化包括:

  • 1. 在math/rand(被math/rand弃用)中的Read方法在math/rand/v2中不再继续使用(在math/rand中仍可用)。大部分调用Read的地方应使用crypto/rand的Read。另外,也可以使用Uint64方法自定义一个Read。
  • 2. 通过顶级函数访问的全局生成器无条件地随机种子。因为API保证了没有固定的结果序列,所以现在可以进行如每个线程的随机生成器状态这样的优化。
  • 3. Source接口现在有一个单独的Uint64方法;没有Source64接口。
  • 4. 许多方法现在使用更快的算法,过去在math/rand中无法采用,因为它们改变了输出流。
  • 5. 在math/rand/v2中,math/rand的顶级函数和方法Intn, Int31, Int31n, Int63, and Int64n的名字更加符合习惯,分别变成了IntN, Int32, Int32N, Int64, 和Int64N。还新增了顶级函数和方法Uint32, Uint32N, Uint64, Uint64N和UintN。
  • 6. 新的通用函数N与Int64N或Uint64N类似,但适用于任何整数类型。例如,从0到5分钟的随机持续时间为rand.N(5*time.Minute)
  • 7. 由math/rand的Source提供的Mitchell & Reeds LFSR生成器已被两个更现代的伪随机生成器源取代:ChaCha8 and PCG。ChaCha8是一个新的,密码学强大的随机数生成器,其效率大致类似于 PCG。ChaCha8是 math/rand/v2顶级函数使用的算法。截至Go 1.22,math/rand的顶级函数(在没有显式设种的情况下)和Go运行时也使用ChaCha8进行随机性。

来看下面这个示例:

package main

import (
	"fmt"
	"math/rand/v2"
	"time"
)

func main() {
	for _ = range 10 {
		duration := rand.N(time.Minute * 5)
		fmt.Println(duration)
		time.Sleep(time.Millisecond * 100)
	}
}

go/version

package main

import (
	"fmt"
	"go/version"
)

func main() {
	current := "go1.21"
	previous := "go1.20"

	for _, v := range []string{current, previous} {
		if !version.IsValid(v) {
			fmt.Println("Versions are not changed")
			return
		}
	}

	switch version.Compare(current, previous) {
	case 0:
		fmt.Println("Versions are the same")
	case 1:
		fmt.Println("Current version is newer")
	case -1:
		fmt.Println("Current version is older")
	}
}

slices

新的函数Concat用于连接多个切片。

那些会缩小切片大小的函数(DeleteDeleteFuncCompactCompactFunc以及Replace)现在将新长度与老长度之间的元素置零。

如果参数i超出范围,Insert现在总会触发panic。之前如果没有要插入的元素,它在这种情况下不会触发panic。

1.22 slices的api如下:

来看几个示例:

1, Concat

package main

import (
	"fmt"
	"slices"
)

func main() {
	xs := []int{1, 2, 3, 4, 5}
	ys := []int{6, 7}
	zs := []int{8, 9, 10}

	all := slices.Concat(xs, ys, zs)
	fmt.Println(all)
}

2, Delete

package main

import (
	"fmt"
	"slices"
)

func main() {
	xs := []int{1, 2, 3, 4, 5}
	ys := slices.Delete(xs, 1, 3)
	fmt.Println(ys)
}

3, Insert

package main

import (
	"fmt"
	"slices"
)

func main() {
	xs := []int{1, 2, 3, 4, 5}
	ys := slices.Insert(xs, 1, 3)
	fmt.Println(ys)
}

4, Replace

package main

import (
	"fmt"
	"slices"
)

func main() {
	xs := []int{1, 2, 3, 4, 5}
	zs := slices.Replace(xs, 1, 4, 2)
	fmt.Println(zs)
}

net/http

net/http也增加了些新特性,不过我还是习惯用gin。

package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Create User"))
	})
	mux.HandleFunc("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
		userId := r.PathValue("id")
		fmt.Fprintf(w, "User ID: %s ", userId)
	})
	http.ListenAndServe(":10097", mux)
}

想了解更多,请仔细阅读官方文档:https://tip.golang.org/doc/go1.22