1、go新版本变化
-
go.mod文件中,
go 1.22 这一行指定了你项目使用的 Go 语言的最低版本
toolchain go1.x.x确保项目在构建时始终使用指定的 Go 版本。这对于团队中的多个开发者,或者 CI/CD 环境来说,能够保证构建环境的一致性,避免不同的 Go 版本带来的潜在问题。如果你和团队成员或不同的构建环境在使用不同版本的 Go,使用toolchain可以统一所有环境的 Go 版本,确保一致性。 -
SwissTable 哈希表实现:
减少 2%-3% 的内存开销
显著提高读写效率
参考了 Rust 语言的实现方式
-
自旋锁(Spin Mutex)引入:
为线程添加自旋状态
降低线程切换开销,提升并发性能
垃圾回收器(GC)改进:
-
Golang1.21的package初始化顺序变更
对所有import的包进行排序,放在一个列表中
-
重复以下步骤,直到所有包都被初始化
找到第一个所有依赖的import包都已经被初始化的包
初始化该包,并从列表中移除
-
context包变动
context包新增了一个WithCancelCause函数,与WithCancel不同,通过WithCancelCause返回的Context,我们可以得到cancel的原因,增加的还有WithDeadlineCause,WithTimeoutCause比如下面示例:
myError := fmt.Errorf("%s", "myError")
ctx, cancel := context.WithCancelCause(context.Background())
cancel(myError)
fmt.Println(ctx.Err()) // returns context.Canceled
fmt.Println(context.Cause(ctx)) // returns myError
另外增加的还有WithoutCancel,它可以继承原 context 中的值(Value),不会被原 context 的取消(cancel)或超时(deadline)影响
// 创建一个带超时的父 context
parentCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
// 从父 context 派生一个不会被取消的 context
ctx := context.WithoutCancel(parentCtx)
// 启动一个 goroutine 使用这个不会被取消的 context
go func(ctx context.Context) {
select {
case <-time.After(3 * time.Second):
fmt.Println("child goroutine finished (not canceled)")
case <-ctx.Done():
fmt.Println("child goroutine canceled:", ctx.Err())
}
}(ctx)
// 等待 2 秒,父 context 已经超时
time.Sleep(5 * time.Second)
fmt.Println("main exit")
- 支持mutiple errors
// 创建两个error
err1 := errors.New("err1")
err2 := errors.New("err2")
// Join 函数将多个错误连接起来
err3 := errors.Join(err1, err2)
fmt.Printf("output err3:\n%v\n", err3)
// 输出
// err1
// err2
// errors.Is多个错误也能判断
if errors.Is(err3, err1) && errors.Is(err3, err2) {
fmt.Println("err3 contains err1 and err2")
}
err4 := fmt.Errorf("wrap error:%w \n", err3)
fmt.Println(errors.Unwrap(err4))
- time包变动
// time包新增格式常量,可以直接用了,不需要自己再定义了
DateTime = "2006-01-02 15:04:05"
DateOnly = "2006-01-02"
TimeOnly = "15:04:05"
2、由于泛型的引入,go自1.18后新增了很多标准库
Go 1.21 引入了以下标准库:
slices:提供泛型切片操作函数,如slices.Sort、slices.Clone等。maps:提供对 map 的通用操作函数,如maps.Clone、maps.Equal等。cmp:提供泛型比较函数,如cmp.Compare、cmp.Less等。
Go 1.22 新增了以下标准库:
math/rand/v2:引入了更高质量和更快的伪随机数生成算法,提供更清晰、一致的 API。go/version:提供用于验证和比较 Go 版本字符串的函数。
Go 1.23 新增了以下标准库:
iter:提供用于迭代的泛型工具。structs:structs.HostLayout用于声明和指定结构体采用主机的内存布局方式unique:unique 会对所有被添加的值进行全局的并发安全缓存,以确保值的唯一性和有效重用。会做到运行时的驻留支持,以此达到开销较佳
slices 包示例
详细参考:slices/example_test.go
package main
import (
"fmt"
"slices"
"strings"
)
func main() {
// Clone: 创建切片的副本
a := []int{1, 2, 3}
b := slices.Clone(a)
fmt.Println("Clone:", b) // 输出: [1 2 3]
fmt.Printf("Origin:%p,Clone: %p\n", a, b) // 输出: 地址,Origin:0x14000418888,Clone: 0x140004188a0
// Equal: 判断两个切片是否相等
fmt.Println("Equal:", slices.Equal(a, b)) // 输出: true
// Contains: 判断切片中是否包含某个元素
fmt.Println("Contains 2:", slices.Contains(a, 2)) // 输出: true
// BinarySearch: 查找元素位置
names := []string{"Alice", "Bob", "Vera"}
n, found := slices.BinarySearch(names, "Vera")
fmt.Println("Vera:", n, found) // 输出: Vera: 2 true
// Index: 获取元素在切片中的索引
fmt.Println("Index of 3:", slices.Index(a, 3)) // 输出: 2
// Insert: 在指定位置插入元素
a = slices.Insert(a, 1, 99)
fmt.Println("After Insert:", a) // 输出: [1 99 2 3]
// Delete: 删除指定范围的元素
a = slices.Delete(a, 1, 3)
fmt.Println("After Delete:", a) // 输出: [1 3]
// Sort: 对切片进行排序
nums := []int{5, 2, 8, 1}
slices.Sort(nums)
fmt.Println("Sorted:", nums) // 输出: [1 2 5 8]
// Compact: 移除相邻重复元素
dup := []int{1, 1, 2, 2, 2, 3}
n := slices.Compact(dup)
fmt.Println("After Compact:", n) // 输出: [1 2 3]
// Reverse: 反转切片
names := []string{"Alice", "Bob", "Charlie"}
slices.Reverse(names)
fmt.Println("Reversed:", names) // 输出: [Charlie Bob Alice]
// Replace: 替换指定范围的元素
names = slices.Replace(names, 1, 2, "David", "Eve")
fmt.Println("After Replace:", names) // 输出: [Charlie David Eve]
// Repeat: 重复切片内容
repeated := slices.Repeat([]int{1, 2}, 3)
fmt.Println("Repeated:", repeated) // 输出: [1 2 1 2 1 2]
// Min: 获取最小值
minVal := slices.Min([]int{3, 1, 4, 2})
fmt.Println("Min:", minVal) // 输出: 1
// Max: 获取最大值
maxVal := slices.Max([]int{3, 1, 4, 2})
fmt.Println("Max:", maxVal) // 输出: 4
// 拼接2个切片
as1 := []int{0, 1, 2, 3}
as2 := []int{4, 5, 6}
concat := slices.Concat(as1, as2)
fmt.Println("concat:", concat) // 输出: [0 1 2 3 4 5 6]
// SortFunc: 使用自定义比较函数排序
strs := []string{"banana", "apple", "cherry"}
slices.SortFunc(strs, func(a, b string) int {
return strings.Compare(a, b)
})
fmt.Println("Custom Sorted:", strs) // 输出: [apple banana cherry]
// SortStableFunc: 稳定排序,保持相等元素的原始顺序
// 对比:sort.Slice:适用于需要不稳定排序的情况,性能可能更优。如果你的需求不关心相等元素的顺序,可以继续使用 sort.Slice。如果需要稳定排序,那么 slices.SortStableFunc 会是一个更合适的选择
type Person struct {
Name string
Age int
}
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Alice", 22},
}
slices.SortStableFunc(people, func(a, b Person) int {
return strings.Compare(a.Name, b.Name)
})
fmt.Println("Stable Sorted:", people)
// 输出: [{Alice 30} {Alice 22} {Bob 25}]
// Chunk:对切片分批
type Person struct {
Name string
Age int
}
type People []Person
people := People{
{"Gopher", 13},
{"Alice", 20},
{"Bob", 5},
{"Vera", 24},
{"Zac", 15},
}
// Chunk people into []Person 2 elements at a time.
for c := range slices.Chunk(people, 2) {
fmt.Println(c)
}
// Output:
// [{Gopher 13} {Alice 20}]
// [{Bob 5} {Vera 24}]
// [{Zac 15}]
}
// maps示例
详细参考:maps/example_test.go
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
m1 := map[string]int{"a": 1, "b": 2}
m2 := maps.Clone(m1)
fmt.Println("Clone:", m2) // 输出: map[a:1 b:2]
fmt.Printf("Origin:%p,Clone: %p\n", m1, m2) // 输出地址:不同,Origin:0x14000a01110,Clone: 0x14000a01140
// Equal: 判断两个 map 是否相等
m3 := map[string]int{"b": 2, "a": 1}
fmt.Println("Equal:", maps.Equal(m1, m3)) // 输出: true
// Keys: 获取 map 的所有键
keys := slices.Sorted(maps.Keys(m1))
fmt.Println("Keys:", keys) // 输出: [a b]
// Values: 获取 map 的所有值
values := slices.Sorted(maps.Values(m1))
fmt.Println("Values:", values) // 输出: [1 2]
// Copy: 将 src 的键值对复制到 dst 中
dst := map[string]int{"a": 10}
src := map[string]int{"b": 20}
maps.Copy(dst, src)
fmt.Println("After Copy:", dst) // 输出: map[a:10 b:20]
// DeleteFunc: 根据条件删除键值对
maps.DeleteFunc(dst, func(k string, v int) bool {
return v > 15
})
fmt.Println("After DeleteFunc:", dst) // 输出: map[a:10]
// Collect collects key-value pairs from seq into a new map
s1 := []string{"zero", "one", "two", "three"}
res := maps.Collect(slices.All(s1))
fmt.Println("collect res is:", res) // 输出: map[0:zero 1:one 2:two 3:three]
}
// cmp示例
package main
import (
"cmp"
"fmt"
)
func main() {
a, b := 3, 7
result := cmp.Compare(a, b)
fmt.Println("Compare(3, 7):", result) // 输出: -1,因为 3 < 7
// 比较两个字符串
x, y := "apple", "banana"
fmt.Println("Compare(apple, banana):", cmp.Compare(x, y)) // 输出: -1
fmt.Println("Less(3, 7):", cmp.Less(3, 7)) // true
// cmp.Or返回第一个不是零值的结果
// string 类型的零值是 ""
name := ""
defaultName := "Guest"
finalName := cmp.Or(name, defaultName)
fmt.Println("Final Name:", finalName) // 输出: "Guest"
// int 类型的零值是 0
count := 0
defaultCount := 10
finalCount := cmp.Or(count, defaultCount)
fmt.Println("Final Count:", finalCount) // 输出: 10
// 如果 v 不是零值,返回自身
nonZero := "Alice"
fmt.Println("Or(\"Alice\", \"Guest\"):", cmp.Or(nonZero, defaultName)) // 输出: "Alice"
}
rand示例
自动使用全局安全种子(不需要再手动调用 rand.Seed())
package main
import (
"fmt"
"math/rand/v2"
)
func main() {
// 比较两个整数
// 生成 [0, 100) 范围的随机整数
n := rand.IntN(100)
fmt.Println("随机整数 [0,100):", n)
fmt.Println(rand.IntN(100))
fmt.Println(rand.IntN(100))
fmt.Println(rand.IntN(100))
// 生成 [0.0, 1.0) 的随机 float64
f := rand.Float64()
fmt.Println("随机 float64:", f)
s := []string{"apple", "banana", "cherry", "date"}
rand.Shuffle(len(s), func(i, j int) {
s[i], s[j] = s[j], s[i]
})
fmt.Println("打乱后的切片:", s)
}
3、使用变化
for...range...变化(Go1.22 )
// 1、for...range支持整数范围输出,不再需要像以前for i:=0;i<10;i++这样写了
package main
import "fmt"
func main() {
for i := range 10 {
// fmt.Println(10 - i) // 输出1到10
fmt.Println(i) // 输出0到9
}
fmt.Println("go1.22 has lift-off!")
}
// "for "循环在迭代之间意外共享循环变量的问题现已得到解决。以下代码将按一定顺序打印 "a"、"b "和 "c",
// 在以前go版本这样写输出的结果是"c","c","c"
func main() {
values := []string{"a", "b", "c"}
for _, val := range values {
go func() {
fmt.Println(val)
}()
}
time.Sleep(time.Second * 3)
}
