Go泛型详解

官网文档:https://go.dev/blog/intro-generics
泛型为该语言添加了三个新的重要功能:

  • 函数和类型的类型参数。
  • 将接口类型定义为类型集,包括没有方法的类型。
  • 类型推断,在许多情况下允许在调用函数时省略类型参数。

类型参数(Type Parameters)

在这里插入图片描述
举例

package main

import "fmt"

func min[T int | float64](a, b T) T { //T int | float64是类型参数列表,可以1-n个
	if a <= b {
		return a
	}
	return b
}

func main() {
	m1 := min[int](1, 2)
	fmt.Println(m1) // 1
	m2 := min[float64](-0.1, -0.2)
	fmt.Println(m2) // -0.2
}

向 min 函数提供类型参数(在本例中为int和float64)称为实例化( instantiation )。
类型实例化分两步进行:

  1. 首先,编译器在整个泛型函数或类型中将所有类型形参(type parameters)替换为它们各自的类型实参(type arguments)。
  2. 其次,编译器验证每个类型参数是否满足相应的约束。
    在成功实例化之后,我们将得到一个非泛型函数,它可以像任何其他函数一样被调用。例如:
fmin := min[float64] // 类型实例化,编译器生成T=float64的min函数
m2 = fmin(1.2, 2.3)  // 1.2

类型参数的使用

除了函数中支持使用类型参数列表外,类型也可以使用类型参数列表。

type Slice[T int | string] []T

type Map[K int | string, V float32 | float64] map[K]V

type Tree[T interface{}] struct {
	left, right *Tree[T]
	value       T
}

在上述泛型类型中,T、K、V都属于类型形参,类型形参后面是类型约束,类型实参需要满足对应的类型约束。
泛型类型可以有方法,例如为上面的Tree实现一个查找元素的Lookup方法。

func (t *Tree[T]) Lookup(x T) *Tree[T] { ... }

要使用泛型类型,必须进行实例化。Tree[string]是使用类型实参string实例化 Tree 的示例。

var stringTree Tree[string]

类型约束

普通函数中的每个参数都有一个类型; 该类型定义一系列值的集合。例如,我们上面定义的非泛型函数minFloat64那样,声明了参数的类型为float64,那么在函数调用时允许传入的实际参数就必须是可以用float64类型表示的浮点数值。

类似于参数列表中每个参数都有对应的参数类型,类型参数列表中每个类型参数都有一个类型约束。类型约束定义了一个类型集——只有在这个类型集中的类型才能用作类型实参。

Go语言中的类型约束是接口类型。

就以上面提到的min函数为例,我们来看一下类型约束常见的两种方式。

类型约束接口可以直接在类型参数列表中使用。

// 类型约束字面量,通常外层interface{}可省略
func min[T interface{ int | float64 }](a, b T) T {
	if a <= b {
		return a
	}
	return b
}

作为类型约束使用的接口类型可以事先定义并支持复用。

// 事先定义好的类型约束类型
type Value interface {
	int | float64
}
func min[T Value](a, b T) T {
	if a <= b {
		return a
	}
	return b
}

在使用类型约束时,如果省略了外层的interface{}会引起歧义,那么就不能省略。例如:

type IntPtrSlice [T *int] []T  // T*int ?

type IntPtrSlice[T *int,] []T  // 只有一个类型约束时可以添加`,`
type IntPtrSlice[T interface{ *int }] []T // 使用interface{}包裹

类型集

Go1.18开始接口类型的定义也发生了改变,由过去的接口类型定义方法集(method set)变成了接口类型定义类型集(type set)。
也就是说,接口类型现在可以用作值的类型,也可以用作类型约束。
在这里插入图片描述
把接口类型当做类型集相较于方法集有一个优势: 我们可以显式地向集合添加类型,从而以新的方式控制类型集。

Go语言扩展了接口类型的语法,让我们能够向接口中添加类型。例如

type V interface {
	int | string | bool
}

在这里插入图片描述
从 Go 1.18 开始,一个接口不仅可以嵌入其他接口,还可以嵌入任何类型、类型的联合或共享相同底层类型的无限类型集合。

当用作类型约束时,由接口定义的类型集精确地指定允许作为相应类型参数的类型。

  • |符号

T1 | T2表示类型约束为T1和T2这两个类型的并集,例如下面的Integer类型表示由Signed和Unsigned组成。

type Integer interface {
	Signed | Unsigned
}
  • ~符号
    ~T表示所以底层类型是T的类型,例如~string表示所有底层类型是string的类型集合。
type MyString string  // MyString的底层类型是string

注意:~符号后面只能是基本类型。

接口作为类型集是一种强大的新机制,是使类型约束能够生效的关键。目前,使用新语法表的接口只能用作类型约束。

any接口
空接口在类型参数列表中很常见,在Go 1.18引入了一个新的预声明标识符,作为空接口类型的别名。

// src/builtin/builtin.go

type any = interface{}

由此,我们可以使用如下代码:

func foo[S ~[]E, E any]() {
	// ...
}

类型推断

对于类型参数,需要传递类型参数,这可能导致代码冗长。回到我们通用的 min函数:

func min[T int | float64](a, b T) T {
	if a <= b {
		return a
	}
	return b
}

类型形参T用于指定a和b的类型。我们可以使用显式类型实参调用它:

var a, b, m float64
m = min[float64](a, b) // 显式指定类型实参

在许多情况下,编译器可以从普通参数推断 T 的类型实参。这使得代码更短,同时保持清晰。

var a, b, m float64

m = min(a, b) // 无需指定类型实参

这种从实参的类型推断出函数的类型实参的推断称为函数实参类型推断。函数实参类型推断只适用于函数参数中使用的类型参数,而不适用于仅在函数结果中或仅在函数体中使用的类型参数。例如,它不适用于像 MakeT T any T 这样的函数,因为它只使用 T 表示结果。

约束类型推断

Go 语言支持另一种类型推断,即约束类型推断。接下来我们从下面这个缩放整数的例子开始:

// Scale 返回切片中每个元素都乘c的副本切片
func Scale[E constraints.Integer](s []E, c E) []E {
    r := make([]E, len(s))
    for i, v := range s {
        r[i] = v * c
    }
    return r
}

这是一个泛型函数适用于任何整数类型的切片。

现在假设我们有一个多维坐标的 Point 类型,其中每个 Point 只是一个给出点坐标的整数列表。这种类型通常会实现一些业务方法,这里假设它有一个String方法。

type Point []int32

func (p Point) String() string {
    b, _ := json.Marshal(p)
    return string(b)
}

由于一个Point其实就是一个整数切片,我们可以使用前面编写的Scale函数:

func ScaleAndPrint(p Point) {
    r := Scale(p, 2)
    fmt.Println(r.String()) // 编译失败
}

不幸的是,这代码会编译失败,输出r.String undefined (type []int32 has no field or method String的错误。

问题是Scale函数返回类型为[]E的值,其中E是参数切片的元素类型。当我们使用Point类型的值调用Scale(其基础类型为[]int32)时,我们返回的是[]int32类型的值,而不是Point类型。这源于泛型代码的编写方式,但这不是我们想要的。

为了解决这个问题,我们必须更改 Scale 函数,以便为切片类型使用类型参数。

func Scale[S ~[]E, E constraints.Integer](s S, c E) S {
    r := make(S, len(s))
    for i, v := range s {
        r[i] = v * c
    }
    return r
}

我们引入了一个新的类型参数S,它是切片参数的类型。我们对它进行了约束,使得基础类型是S而不是[]E,函数返回的结果类型现在是S。由于E被约束为整数,因此效果与之前相同:第一个参数必须是某个整数类型的切片。对函数体的唯一更改是,现在我们在调用make时传递S,而不是[]E。

现在这个Scale函数,不仅支持传入普通整数切片参数,也支持传入Point类型参数。

这里需要思考的是,为什么不传递显式类型参数就可以写入 Scale 调用?也就是说,为什么我们可以写 Scale(p, 2),没有类型参数,而不是必须写 Scale[Point, int32](p, 2) ?

新 Scale 函数有两个类型参数——S 和 E。在不传递任何类型参数的 Scale(p, 2) 调用中,如上所述,函数参数类型推断让编译器推断 S 的类型参数是 Point。但是这个函数也有一个类型参数 E,它是乘法因子 c 的类型。相应的函数参数是2,因为2是一个非类型化的常量,函数参数类型推断不能推断出 E 的正确类型(最好的情况是它可以推断出2的默认类型是 int,而这是错误的,因为Point 的基础类型是[]int32)。相反,编译器推断 E 的类型参数是切片的元素类型的过程称为约束类型推断

约束类型推断从类型参数约束推导类型参数。当一个类型参数具有根据另一个类型参数定义的约束时使用。当其中一个类型参数的类型参数已知时,约束用于推断另一个类型参数的类型参数。

通常的情况是,当一个约束对某种类型使用 ~type 形式时,该类型是使用其他类型参数编写的。我们在 Scale 的例子中看到了这一点。S 是 ~[]E,后面跟着一个用另一个类型参数写的类型[]E。如果我们知道了 S 的类型实参,我们就可以推断出E的类型实参。S 是一个切片类型,而 E是该切片的元素类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/793107.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二分查找算法——部分OJ题详解

目录 关于二分查找算法 部分OJ题详解 704.二分查找 一&#xff0c;分析题目 二&#xff0c;细节处理 三&#xff0c;题目代码 四&#xff0c;*总结朴素模板 *34.在排序数组中查找元素的第一个和最后一个位置 一&#xff0c;查找左端点 二&#xff0c;处理左端点细…

HTML速成学习总结

一、HTML开始 简介&#xff1a; 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用于创建网页的标准标记语言。 初始网页&#xff1a; <!doctype html> <html><head><meta …

【Java数据结构】初识线性表之一:顺序表

使用Java简单实现一个顺序表 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 线性表大致包含如下的一些方法&#xff1a; public class MyArrayList { private int[] array; pri…

FastAPI 学习之路(三十五)项目结构优化

之前我们创建的文件都是在一个目录中&#xff0c;但是在我们的实际开发中&#xff0c;肯定不能这样设计&#xff0c;那么我们去创建一个目录&#xff0c;叫models&#xff0c;大致如下。 主要目录是&#xff1a; __init__.py 是一个空文件&#xff0c;说明models是一个package…

2.线性回归

简化的房价模型 假设1&#xff1a;影响房价的关键因素时卧室个数&#xff0c;卫生间和居住面积&#xff0c;记为 x 1 , x 2 , x 3 x_1,x_2,x_3 x1​,x2​,x3​ 假设2&#xff1a;成交价时关键因素的加权和&#xff1a; y w 1 x 1 w 2 x 2 w 3 x 3 b y w_1x_1w_2x_2w_3x…

【LeetCode:1071. 字符串的最大公因子 + 模拟 + 最大公约数】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

javaweb中的请求与响应--基于postman工具的应用(附带postman的详细安装步骤)

一、前言 后端的第一天感觉难度就上来了&#xff0c;可能是基础太过薄弱了吧。目前看视频已经有点跟不上了&#xff0c;果然15天想要拿下还是太勉强了点。30天还差不多。不知道读者们有没有好好的去学这方面的知识&#xff0c;没有什么是学不会的&#xff0c;关键是坚持。 Po…

# Redis 入门到精通(一)数据类型(3)

Redis 入门到精通&#xff08;一&#xff09;数据类型&#xff08;3&#xff09; 一、redis 数据类型–set 类型介绍与基本操作 1、set 类型 新的存储需求: 存储大量的数据&#xff0c;在查询方面提供更高的效率。需要的存储结构: 能够保存大量的数据&#xff0c;高效的内部…

飞睿智能6公里WiFi图传接收模块,低延迟、抗干扰、高速稳定传输数据,无人机、农田远距离WiFi模块

在科技日新月异的今天&#xff0c;无线通信技术正以前所未有的速度发展&#xff0c;不仅改变了我们的生活方式&#xff0c;还为企业带来了前所未有的商业机遇。今天&#xff0c;我要向大家介绍一款飞睿智能的产品——6公里WiFi图传接收模块&#xff0c;它以其高性能、稳定的传输…

华为od100问持续分享-1

我是一名软件开发培训机构老师&#xff0c;我的学生已经有上百人通过了华为OD机试&#xff0c;学生们每次考完试&#xff0c;会把题目拿出来一起交流分享。 重要&#xff1a;2024年5月份开始&#xff0c;考的都是OD统一考试&#xff08;D卷&#xff09;&#xff0c;题库已经整…

国漫推荐10

玄幻、恋爱 1.《两不疑》古风、恋爱 2.《中国古诗词动漫》 3.《武神主宰》 4.《百妖谱》 5.《灵剑尊》 6.《万界仙踪》 7.《万界神主》 8.《武庚纪》 9.《无上神帝》

全网最适合入门的面向对象编程教程:14 类和对象的 Python 实现-类的静态方法和类方法,你分得清吗?

全网最适合入门的面向对象编程教程&#xff1a;14 类和对象的 Python 实现-类的静态方法和类方法&#xff0c;你分得清吗&#xff1f; 摘要&#xff1a; 本文主要介绍了Python中类和对象中的类方法和静态方法&#xff0c;以及类方法和静态方法的定义、特点、应用场景和使用方…

轻松搭建 VirtualBox + Vagrant + Linux 虚拟机

一、准备工作 首先&#xff0c;我们来了解一下搭建 VirtualBox Vagrant Linux 虚拟机所需的软件准备工作。 VirtualBox 的下载地址&#xff1a;您可以通过访问https://www.virtualbox.org/wiki/Downloads获取适用于您系统的版本。 Vagrant 的下载地址&#xff1a;前往http…

5款常用的漏洞扫描工具,网安人员不能错过!

漏洞扫描是指基于漏洞数据库&#xff0c;通过扫描等手段对指定的远程或者本地计算机系统的安全脆弱性进行检测&#xff0c;发现可利用漏洞的一种安全检测的行为。 在漏洞扫描过程中&#xff0c;我们经常会借助一些漏扫工具&#xff0c;市面上漏扫工具众多&#xff0c;其中有一…

数学建模·Topsis优劣解距离法

Topsis优劣解 一种新的评价方法&#xff0c;特点就是利用原有数据&#xff0c;客观性强。相较于模糊评价和层次评价 更加客观&#xff0c;充分利用原有数据&#xff0c;精确反映方案差距基本原理 离最优解最近&#xff0c;离最劣解越远具体步骤 正向化 代码与原理与熵权法…

Docker 使用基础(3)—容器

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;秒針を噛む—ずっと真夜中でいいのに。 0:34━━━━━━️&#x1f49f;──────── 4:20 &#x1f504; ◀️ ⏸ …

LLM基础模型系列:Fine-Tuning总览

由于对大型语言模型&#xff0c;人工智能从业者经常被问到这样的问题&#xff1a;如何训练自己的数据&#xff1f;回答这个问题远非易事。生成式人工智能的最新进展是由具有许多参数的大规模模型驱动的&#xff0c;而训练这样的模型LLM需要昂贵的硬件&#xff08;即许多具有大量…

万字长文!流行 AI 视频生成大模型介绍 浅体验

目录 国外 AI 视频生成大模型Sora——值得期待的引领者官方描述拥有强大的能力一经发布&#xff0c;立即爆火不同业内人士的评价周鸿祎的评价陈楸帆的评价 值得期待的引领者 Dream Machine——宣传虽好&#xff0c;但仍需努力新兴的 AI 视频生成大模型媒体强烈的追捧实测体验&a…

看番工具 -- oneAnime v1.2.5绿色版

软件简介 OneAnime是一款专为动漫爱好者设计的应用程序&#xff0c;它提供了一个庞大的动漫资源库&#xff0c;用户可以在这里找到各种类型的动漫&#xff0c;包括热门的、经典的、新番的等等。OneAnime的界面设计简洁明了&#xff0c;操作方便&#xff0c;用户可以轻松地搜索…

企业微信与大量外部成员的即时消息沟通和文档协作解决方案

背景 公司使用企业微信&#xff0c;现在有部门需要招聘大量外包成员&#xff0c;但是不希望外包成员进入公司企微的组织架构&#xff0c;要实现公司与外包成员的即时消息沟通和管理&#xff0c;以及文档共享协作。 痛点 虽然企微可以将外包成员的微信加为外部联系人&#xf…