Go 1.18 泛型分析

569 阅读3分钟

Go 终于在1.18版本中引入了泛型实现方式。分析下泛型的实现过程。

首先, 我看到的是一段泛型示例:

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))
}

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

这段代码取自:Tutorial: Getting started with generics - The Go Programming Language

上面的代码看了后,有如下疑问:

泛型有两个场景:

  • 定义函数的时候

    func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {}

问:上面的函数签名中 K comparable 是否是必须的?

问: comparable 是否是定义泛型包含对象 int64 float64 都必须具有的属性限制?

问: 函数签名返回值类型中 map[K]V的 K 是否是使用泛型后,返回值签名里必须有的?

  • 定义接口的时候( Number 是接口)

    func SumNumbers[K comparable, V Number](m map[K]V) V {}

问:使用接口定义泛型的时候, K comparable 是否是必须的?

问:使用接口定义泛型的时候,函数签名返回值类型中 map[K]V的 K 是否是必须的?

针对上面的问题,我本来首先想到的是查资料, 但是由于Go1.18刚发布, 网上的资料几乎都是官方文档那一篇文章(或者它的翻译)。所以只能本地安装Go1.18, 然后在本地来验证。

为了分析上面的问题, 我重写了上面的代码,结果如下:

package main

import "fmt"

type Number interface {
    int64 | float64
}

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

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


    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))
}

// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats[V int64 | float64](m []V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers[V Number](m []V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

执行结果如下:

从上面的结果, 上面的问题可以得到如下结论:

问:上面的函数签名中 **K comparable** 是否是必须的?

  • 答:不是必须的

问: **comparable** 是否是定义泛型包含对象 **int64** **float64** 都必须具有的属性限制?

  • 答:虽然在签名里面 **comparable** 不是必须的, 但是 **comparable** 是是定义泛型包含对象 **int64** **float64** 都必须具有的属性, 不然不能对 **int64** **float64** 做累加操作

问: 函数签名返回值类型中 **map**``**[K]V** **K** 是否是使用泛型后,返回值签名里必须有的?

  • 答:不是必须的

问:使用接口定义泛型的时候, **K comparable** 是否是必须的?

  • 答:不是必须的

问:使用接口定义泛型的时候,函数签名返回值类型中 **map**``**[K]V** **K** 是否是必须的?

  • 答:不是必须的

后续再对 模糊测试 workspace 工作区 进行挨个分析。