官方说明

官方说明比较抽象,以下通过例子来说明实际的变化。

部分初始化的泛型函数(Partially instantiated generic functions)

如以下示例,IsEven是个泛型函数,可以作为非泛型函数Any和泛型函数AnyGeneric的参数。

func IsEven[T int | int8 | int16](n T) bool {
    return n%2 == 0
}

func Any(numbers []int, fn func(int) bool) bool {
    for _, v := range numbers {
        if fn(v) {
            return true
        }
    }
    return false
}

func AnyGeneric[T any](numbers []T, fn func(T) bool) bool {
    for _, v := range numbers {
        if fn(v) {
            return true
        }
    }
    return false
}

在Go1.21中,我们可以这些调用

// This works in Go 1.21
// In Go 1.20, you will get:
//   "cannot use generic function IsEven without instantiation"
anyEven := Any(numbers, IsEven)
anyEven = AnyGeneric(numbers, IsEven)

而在Go1.20中,我们只能这么写

anyEven := Any(numbers, IsEven[int])
anyEven = AnyGeneric(numbers, IsEven[int])

Go1.21可以从函数的泛型参数中推导出AnyGeneric类型参数的实际类型。

接口赋值给接口(interface assignment interface)

如下面的例子,接口RandomElement返回一个随机数。它有一个泛型实现MyList,也有一个非泛型的实现IPSetMustRandom是个泛型函数,会消费该接口,使用该接口返回一个随机数,如果没有数据,就panic。

type RandomElement[T any] interface {
    // 返回一个随机数
    RandomElement() (T, bool)
}

func MustRandom[T any](collection RandomElement[T]) T {
    val, ok := collection.RandomElement()
    if !ok {
        panic("collection is empty")
    }
    return val
}

// MyList 是自定义的泛型类型
type MyList[T any] []T

func (l MyList[T]) RandomElement() (T, bool) {
    if len(l) == 0 {
        var zero T
        return zero, false
    }
    // assume the 0 is a random number
    return l[0], true
}

// IPSet是个具体类型
type IPSet map[netip.Addr]bool

func (s IPSet) RandomElement() (netip.Addr, bool) {
    for k := range s {
        // assume the first element is a random element
        return k, true
    }
    return netip.Addr{}, false
}

在Go1.21中可以这么写

// 作为调用方MustRandom函数,可以从参数类型推断出泛型函数的类型
// In Go 1.20, you will get:
//   "type MyList[int] of MyList[int]{…} does not match RandomElement[T] (cannot infer T)"
randomInt := MustRandom(MyList[int]{1, 2, 3})
var ipSet = make(IPSet)
ipSet[netip.Addr{}] = true
// // In Go 1.20, you will get:
// //   "type IPSet of ipSet does not match RandomElement[T] (cannot infer T)"
randomIP := MustRandom(ipSet)

而在Go1.20中,我们只能这么写

// In Go 1.20 you would have to do:
// 调用方和传入参数的泛型函数都需要明确指明实际类型
randomInt := MustRandom[int](MyList[int]{1, 2, 3})
randomIP := MustRandom[netip.Addr](ipSet)

Go1.21之后,调用方函数MustRandom知道自己的TRandomElement是一样的,而RandomElement在传入参数中知道自己的类型是intMustRandom自然也能推导出类型参数的具体类型是什么了。

对无类型常量的类型推断(Type inference for untyped constants)

如以下的例子,泛型函数Add的参数可能是int,也可能是float64。这里的T就是个untyped constants

func Add[T int | float64](a, b T) T {
    return a + b
}

在go语言中,如下的x, y都是typed常量,我们知道x是int,而y是float64

var x = 1 + 2
var y = 1 + 2.5

但是以下的方法调用,在Go1.20之前,会造成运行时失败,但在Go1.21之后就可以正常推断出z的类型是float64

// In Go 1.20, you will get:
//  default type float64 of 2.5 does not match inferred type int for T
var z = Add(1, 2.5)

参考