探讨Go 1.18泛型在实践中的潜在限制因素?

2026-05-19 19:561阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1193个文字,预计阅读时间需要5分钟。

探讨Go 1.18泛型在实践中的潜在限制因素?

前言:Go 1.18 版本之后正式引入泛型,它被称为类型参数(type parameters)。本文初步介绍 Go 中泛型的使用。

长期以来,Go 都没有泛型的概念,只有接口(interface)和类似接口的空接口(empty interface)等类似泛型的功能。这些功能虽然可以模拟泛型的一些效果,但局限性较大。

从 Go 1.18 版本开始,Go 正式引入了泛型,使得开发者可以更方便地编写可复用的代码。泛型允许在定义函数、方法或类型时使用类型参数,从而实现类似模板编程的效果。

前言

Go 1.18 版本之后正式引入泛型,它被称作类型参数(type parameters),本文初步介绍 Go 中泛型的使用。长期以来 go 都没有泛型的概念,只有接口 interface 偶尔类似的充当泛型的作用,然而接口终究无法满足一些基本的泛型需求,比如这篇文章里,我们会尝试用 Go 的泛型循序渐进地实现一些常见的函数式特性,从而探索 Go 泛型的优势和不足。

Go 1.18

在 Go1.18 可以通过如下命令安装体验:

go install golang.org/dl/go1.18@latest go1.18 download 例1: 泛型版本的求和函数

import ( "golang.org/x/exp/constraints" ) func Sum[T constraints.Integer](values ...T) T { var sum T for _, v := range values { sum += v } return sum }

constraints 原本是放在标准库的包,但是近期被移除了,改到了 x/exp 中,参见 #50792

这个版本实现了对任意多个同类型的整数求和。Sum 后面的中括号 [] 内就是定义类型参数的地方,其中 T 为类型参数名,constraints.Integer 是对该类型参数的约束,即 T 应该满足的条件,在这里我们要求 T 是一个整数。剩下的代码就和普通没有泛型的代码一致了,只不过后面 T 可以当作一个类型来使用。

泛型语法
  • 函数名后可以附带一个方括号,包含了该函数涉及的类型参数(Type Paramters)的列表:func F[T any](p T) { ... }

    探讨Go 1.18泛型在实践中的潜在限制因素?

  • 这些类型参数可以在函数参数和函数体中(作为类型)被使用

  • 自定义类型也可以有类型参数列表:type M[T any] []T

  • 每个类型参数对应一个类型约束,上述的 any 就是预定义的匹配任意类型的约束

  • 类型约束在语法上以 interface 的形式存在,在 interface 中嵌入类型 T 可以表示这个类型必须是 T:

type Integer1 interface { int }

  • 嵌入单个类型意义不大,我们可以用 | 来描述类型的 union:

type Integer2 interface { int | int8 | int16 | int32 | int64 }

  • ~T 语法可以表示该类型的「基础类型」是 T,比如说我们的自定义类型 type MyInt int 不满足上述的 Integer1 约束,但满足以下的约束:

type Integer3 interface { ~int } 高阶函数实例

filter 操作是高阶函数的经典应用,它接受一个函数 f(func (T) bool)和一个线性表 l([] T),对 l 中的每个元素应用函数 f,如结果为 true,则将该元素加入新的线性表里,否则丢弃该元素,最后返回新的线性表。

func Filter[T any](f func(T) bool, src []T) []T { var dst []T for _, v := range src { if f(v) { dst = append(dst, v) } } return dst } func main() { src := []int{-2, -1, -0, 1, 2} dst := Filter(func(v int) bool { return v >= 0 }, src) fmt.Println(dst) } // Output: // [0 1 2] 让人开心的改变 : ) 实现一个三元操作

众所周知Go语言不支持三元运算符操作,现在有了泛型,让我们来模拟一个:

// IFF if yes return a else b func IFF[T any](yes bool, a, b T) T { if yes { return a } return b } // IFN if yes return func, a() else b(). func IFN[T any](yes bool, a, b func() T) T { if yes { return a() } return b() } func main() { a := -1 assert.Equal(t, utils.IFF(a > 0, a, 0), 0) assert.Equal(t, utils.IFN(a > 0, func() int { return a }, func() int { return 0 }), 0) } 令人沮丧

本文共计1193个文字,预计阅读时间需要5分钟。

探讨Go 1.18泛型在实践中的潜在限制因素?

前言:Go 1.18 版本之后正式引入泛型,它被称为类型参数(type parameters)。本文初步介绍 Go 中泛型的使用。

长期以来,Go 都没有泛型的概念,只有接口(interface)和类似接口的空接口(empty interface)等类似泛型的功能。这些功能虽然可以模拟泛型的一些效果,但局限性较大。

从 Go 1.18 版本开始,Go 正式引入了泛型,使得开发者可以更方便地编写可复用的代码。泛型允许在定义函数、方法或类型时使用类型参数,从而实现类似模板编程的效果。

前言

Go 1.18 版本之后正式引入泛型,它被称作类型参数(type parameters),本文初步介绍 Go 中泛型的使用。长期以来 go 都没有泛型的概念,只有接口 interface 偶尔类似的充当泛型的作用,然而接口终究无法满足一些基本的泛型需求,比如这篇文章里,我们会尝试用 Go 的泛型循序渐进地实现一些常见的函数式特性,从而探索 Go 泛型的优势和不足。

Go 1.18

在 Go1.18 可以通过如下命令安装体验:

go install golang.org/dl/go1.18@latest go1.18 download 例1: 泛型版本的求和函数

import ( "golang.org/x/exp/constraints" ) func Sum[T constraints.Integer](values ...T) T { var sum T for _, v := range values { sum += v } return sum }

constraints 原本是放在标准库的包,但是近期被移除了,改到了 x/exp 中,参见 #50792

这个版本实现了对任意多个同类型的整数求和。Sum 后面的中括号 [] 内就是定义类型参数的地方,其中 T 为类型参数名,constraints.Integer 是对该类型参数的约束,即 T 应该满足的条件,在这里我们要求 T 是一个整数。剩下的代码就和普通没有泛型的代码一致了,只不过后面 T 可以当作一个类型来使用。

泛型语法
  • 函数名后可以附带一个方括号,包含了该函数涉及的类型参数(Type Paramters)的列表:func F[T any](p T) { ... }

    探讨Go 1.18泛型在实践中的潜在限制因素?

  • 这些类型参数可以在函数参数和函数体中(作为类型)被使用

  • 自定义类型也可以有类型参数列表:type M[T any] []T

  • 每个类型参数对应一个类型约束,上述的 any 就是预定义的匹配任意类型的约束

  • 类型约束在语法上以 interface 的形式存在,在 interface 中嵌入类型 T 可以表示这个类型必须是 T:

type Integer1 interface { int }

  • 嵌入单个类型意义不大,我们可以用 | 来描述类型的 union:

type Integer2 interface { int | int8 | int16 | int32 | int64 }

  • ~T 语法可以表示该类型的「基础类型」是 T,比如说我们的自定义类型 type MyInt int 不满足上述的 Integer1 约束,但满足以下的约束:

type Integer3 interface { ~int } 高阶函数实例

filter 操作是高阶函数的经典应用,它接受一个函数 f(func (T) bool)和一个线性表 l([] T),对 l 中的每个元素应用函数 f,如结果为 true,则将该元素加入新的线性表里,否则丢弃该元素,最后返回新的线性表。

func Filter[T any](f func(T) bool, src []T) []T { var dst []T for _, v := range src { if f(v) { dst = append(dst, v) } } return dst } func main() { src := []int{-2, -1, -0, 1, 2} dst := Filter(func(v int) bool { return v >= 0 }, src) fmt.Println(dst) } // Output: // [0 1 2] 让人开心的改变 : ) 实现一个三元操作

众所周知Go语言不支持三元运算符操作,现在有了泛型,让我们来模拟一个:

// IFF if yes return a else b func IFF[T any](yes bool, a, b T) T { if yes { return a } return b } // IFN if yes return func, a() else b(). func IFN[T any](yes bool, a, b func() T) T { if yes { return a() } return b() } func main() { a := -1 assert.Equal(t, utils.IFF(a > 0, a, 0), 0) assert.Equal(t, utils.IFN(a > 0, func() int { return a }, func() int { return 0 }), 0) } 令人沮丧