go 1.18 beta 泛型终于来了

1,361 阅读5分钟

注意:这是测试版内容。

本教程介绍了 Go 中泛型的基础知识。使用泛型,您可以声明和使用编写为与调用代码提供的一组类型中的任何类型一起使用的函数或类型。

在本教程中,您将声明两个简单的非泛型函数,然后在单个泛型函数中捕获相同的逻辑。

您将学习以下部分:

  1. 为您的代码创建一个文件夹。
  2. 添加非通用函数。
  3. 添加通用函数来处理多种类型。
  4. 调用泛型函数时删除类型参数。
  5. 声明一个类型约束。

必要条件

  • Go 1.18 Beta 1 或更高版本的安装。 有关安装说明,请参阅安装和使用测试版
  • 一种编辑代码的工具。 您拥有的任何文本编辑器都可以正常工作。
  • 命令终端。 Go 适用于 Linux 和 Mac 上的任何终端,以及 Windows 中的 PowerShell 或 cmd。

安装和使用测试版

本教程需要 Beta 1 中可用的泛型功能。要安装 Beta,请执行以下步骤:

  1. 运行以下命令安装测试版。

    $ go install golang.org/dl/go1.18beta1@latest
    
  2. 运行以下命令下载更新。

    $ go1.18beta1 download
    
  3. go使用测试版而不是 Go 的发布版本(如果你有的话)运行命令。

    你可以通过使用 beta 名称或通过将 beta 别名为另一个名称来运行带有 beta 的命令。

    • 使用 beta 名称,您可以通过调用go1.18beta1 而不是调用来运行命令go

      $ go1.18beta1 version
      
    • 通过将 beta 名称别名为另一个名称,您可以简化命令:

      $ alias go=go1.18beta1
      $ go version
      

本教程中的命令将假定您为 beta 名称设置了别名。

例子:

func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}
  • 声明一个SumIntsOrFloats函数有两个类型参数(方括号中),KV,以及使用该类型参数,一个参数m类型的map[K]V。该函数返回一个类型的值V
  • K类型参数指定类型约束comparable。专门针对此类情况,comparable在 Go中预先声明了约束。它允许其值可用作比较运算符==以及!=等。Go 要求映射键具有可比性。因此,声明Kcomparable是必要的。
  • V类型参数指定一个约束,它支持两种类型int64float64。使用|指定了两种类型的联合,这意味着此约束允许任一类型。编译器将允许任一类型作为调用代码中的参数。
  • 指定m参数是 type map[K]V,其中KV 是已经为类型参数指定的类型。请注意,我们知道map[K]V是有效的map类型,因为K是可比较的类型。如果我们没有声明K可比较,编译器将拒绝对map[K]V.

然后再main.go中,使用如下代码输出即可实现不同类型求和:

func main(){

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

}

声明类型约束

在最后一节中,您将把之前定义的约束移到它自己的接口中,以便您可以在多个地方重用它。以这种方式声明约束有助于简化代码,例如当约束更复杂时。

您将类型约束声明为接口。约束允许实现接口的任何类型。例如,如果您声明一个具有三个方法的类型约束接口,然后在泛型函数中将其与类型参数一起使用,则用于调用该函数的类型参数必须具有所有这些方法。

正如您将在本节中看到的,约束接口也可以引用特定类型。

  1. 就在上面main,紧接在 import 语句之后,粘贴以下代码以声明类型约束。

    type Number interface {
        int64 | float64
    }
    

    在此代码中,您:

    • 声明Number要用作类型约束的接口类型。

    • 声明的联合int64float64接口内。

      本质上,您正在将联合从函数声明移动到新的类型约束中。这样,当您想将类型参数限制为int64或 时float64,您可以使用此Number 类型约束而不是写出int64 | float64

  2. 在您已有的函数下方,粘贴以下通用 SumNumbers函数。

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

    在此代码中,您:

    • 使用与您之前声明的泛型函数相同的逻辑声明一个泛型函数,但使用新的接口类型而不是联合作为类型约束。和以前一样,您使用类型参数作为参数和返回类型。
  3. 在 main.go 中,在您已有的代码下方,粘贴以下代码。

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

    在此代码中,您:

    • 调用SumNumbers每个地图,打印每个地图的值的总和。

      与上一节一样,在对泛型函数的调用中省略了类型参数(方括号中的类型名称)。Go 编译器可以从其他参数推断类型参数。

结论

以上是对泛型的简单介绍,但目前go泛型依然除以beta阶段,可能还有一些功能逐渐挖完善,大家尽情期待!

泛型中还封装了constraints.Integerconstraints.Float等接口,以允许更多的数字类型。