在响了数所后,Go的泛型终于正式落地 1.18 版本。而本次更新中最引人注目的无疑就是泛型的引入。虽然Go创始人反对泛型,但最后还是来了。

一、泛型

1.简单的泛型

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

func main() {
    fmt.Println(Add(12, 22))
}

如上述代码中,函数 Add 后的方括号内,即是泛型的定义。该函数定义了一个泛型 T 可以为 int 或 float 。当然也可以加上更多,使用 | 隔开即可。

可是如果泛型可用的类型很多,那函数的定义岂不是会变的很臃肿?
当然,也有办法。

type Addable interface {
    uint|int|flaot64|float32
}

func Add[T Addable] (a, b T) T {
    return a + b
}

func main() {
    fmt.Println(Add(12, 22))
}

由此可见,泛型是可以放在接口内的。

2.字典上的泛型

在官方文档的示例中,使用了字典来介绍泛型

type Number interface {
    int64 | float64
}

func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

comparable 是 1.18 内置的一个新接口,源码定义如下

//comparable 是由所有可比较类型实现的接口
//(布尔、数字、字符串、指针、信道、数组、等类型均为可 comparable 的实例)。
//comparable 接口只能用作泛型约束,不可作为变量的类型。
type comparable interface{ comparable }

在加头看上述的 SumNumbers 函数,其定义了两个泛型,K 和 V,而 K 和 V 可能组成的 map 有很多,比如 map[string]intmap[string]float64 在或者 map[int]int 等等。
当然,我们在调用 SumNumbers 时,也可定义其好它的类型。比如:SumNumbers[int, float64](map[int]float64{})

但大多数情况下,我们并不用手动去告诉编译器我们所用的类型,因为它会自动判断类型。

package main

import "fmt"

type Number interface {
    int64 | float64
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first": 34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first": 35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))

    fmt.Printf("Generic Sums: %v and %v\n",
        SumIntsOrFloats[string, int64](ints),
        SumIntsOrFloats[string, float64](floats))

    fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
        SumIntsOrFloats(ints),
        SumIntsOrFloats(floats))

    fmt.Printf("Generic Sums with Constraint: %v and %v\n",
        SumNumbers(ints),
        SumNumbers(floats))
}

// 未使用泛型的函数。
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// 未使用泛型的函数。
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

// 使用泛型
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}