Go1.21侯选版已发布

官方Blog https://go.dev/blog/go1.21rc

部分原文如下:

Standard library additions

  • New log/slog package for structured logging.
  • New slices package for common operations on slices of any element type. This includes sorting functions that are generally faster and more ergonomic than the sort package.
  • New maps package for common operations on maps of any key or element type.
  • New cmp package with new utilities for comparing ordered values.

其中 slices, maps, cmp都是应用了泛型的标准库

slices & cmp

cmp主要是提供可比较的类型和函数

1
2
3
type Ordered
func Compare[T Ordered](x, y T) int 
func Less[T Ordered](x, y T) bool

Ordered类型的定义

1
2
3
4
5
6
type Ordered interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 |
		~string
}

slices提供了很多有用的工具函数

 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
func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool)
func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool)
func Clip[S ~[]E, E any](s S) S
func Clone[S ~[]E, E any](s S) S
func Compact[S ~[]E, E comparable](s S) S
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S
func Compare[S ~[]E, E cmp.Ordered](s1, s2 S) int
func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int
func Contains[S ~[]E, E comparable](s S, v E) bool
func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool
func Delete[S ~[]E, E any](s S, i, j int) S
func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S
func Equal[S ~[]E, E comparable](s1, s2 S) bool
func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool
func Grow[S ~[]E, E any](s S, n int) S
func Index[S ~[]E, E comparable](s S, v E) int
func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int
func Insert[S ~[]E, E any](s S, i int, v ...E) S
func IsSorted[S ~[]E, E cmp.Ordered](x S) bool
func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool
func Max[S ~[]E, E cmp.Ordered](x S) E
func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E
func Min[S ~[]E, E cmp.Ordered](x S) E
func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S
func Reverse[S ~[]E, E any](s S)
func Sort[S ~[]E, E cmp.Ordered](x S)
func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int)
func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int)

可以看到slices使用到了cmp里的类型和函数

和老的sort库做一个简单的比较,秀一下泛型的存在感

sort库针对不同的类型需要调用不同的函数,后者使用反射,看看sort的编码方式

 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
package main

import (
	"fmt"
	"sort"
)

type People struct {
	Name string
	Age  int
}

func main() {
	// string切片排序
	strs := []string{"Go", "Bravo", "Gopher", "Alpha", "Grin", "Delta"}
	sort.Strings(strs)
	fmt.Println(strs)

	// int切片排序
	ints := []int{5, 2, 6, 3, 1, 4} // unsorted
	sort.Ints(ints)
	fmt.Println(ints)

	// 其他类型(sort.Slice使用了反射)
	people := []People{
		{"Gopher", 7},
		{"Alice", 55},
		{"Vera", 24},
		{"Bob", 75},
	}
	sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name })
	fmt.Println("By name:", people)

	sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
	fmt.Println("By age:", people)
}

slices库的排序编码方式

 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
41
42
43
44
45
46


// 其他类型
package main

import (
	"fmt"
	"slices"
)

type People struct {
	Name string
	Age  int
}

func main() {
	// Ordered类型(int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr,float32,float64,string)的排序

	// string切片排序
	strs := []string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
	slices.Sort(strs)
	fmt.Println(strs)

	// int切片排序
	ints := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
	slices.Sort(ints)
	fmt.Println(ints)

	// 其他类型(使用泛型)
	peoples := []People{
		{"Gopher", 7},
		{"Alice", 55},
		{"Vera", 24},
		{"Bob", 75},
	}
	slices.SortStableFunc(peoples, func(pi, pj People) int {
		if pi.Name < pj.Name {
			return -1
		}
		return 1
	})
	fmt.Println("By name:", peoples)

	slices.SortFunc(peoples, func(pi, pj People) int { return pi.Age - pj.Age })
	fmt.Println("By age:", peoples)
}

maps

提供了对map的一些基本操作

1
2
3
4
5
6
7
func Clone[M ~map[K]V, K comparable, V any](m M) M
func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2)
func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool)
func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool
func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool
func Keys[M ~map[K]V, K comparable, V any](m M) []K
func Values[M ~map[K]V, K comparable, V any](m M) []V

扩展

在Go里面泛型的使用越来越广泛,这里推荐一些泛型应用的三方库

https://github.com/deckarep/golang-set

使用map实现set的一些功能,特别是集合运算

 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
// Syntax example, doesn't compile.
mySet := mapset.NewSet[T]() // where T is some concrete comparable type.

// Therefore this code creates an int set
mySet := mapset.NewSet[int]()

// Or perhaps you want a string set
mySet := mapset.NewSet[string]()

type myStruct struct {
  name string
  age uint8
}

// Alternatively a set of structs
mySet := mapset.NewSet[myStruct]()

// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety.
mySet := mapset.NewSet[any]()

// Union
all := required.Union(sciences).Union(electives).Union(bonus)

// Difference
notScience := all.Difference(sciences)

// Intersect
reqScience := sciences.Intersect(required)

https://github.com/samber/lo

lo同样是为slices, maps, channels提供了大量的工具,Lodash风格。

 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

// Uniq
names := lo.Uniq[string]([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}

// UniqBy
uniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i%3
})
// []int{0, 1, 2}

// Union
union := lo.Union([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}, []int{0, 10})
// []int{0, 1, 2, 3, 4, 5, 10}

// Intersect
result2 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})
// []int{0}

// Difference
left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6})
// []int{1, 3, 4, 5}, []int{6}

// Without
subset := lo.Without([]int{0, 2, 10}, 0, 1, 2, 3, 4, 5)
// []int{10}