Go1.18 的泛型是闹得沸沸扬扬,虽然之前写过很多篇针对泛型的一些设计和思考。但因为泛型的提案之前一直还没定型,所以就没有写完整介绍。
如今已经基本成型,就由煎鱼带大家一起摸透 Go 泛型。本文内容主要涉及泛型的 3 大核心概念,非常值得大家深入了解。
如下:
类型参数,这个名词。不熟悉的小伙伴咋一看就懵逼了。
泛型代码是使用抽象的数据类型编写的,我们将其称之为类型参数。当程序运行通用代码时,类型参数就会被类型参数所取代。也就是类型参数是泛型的抽象数据类型。
简单的泛型例子:
func Print(s []T) {
for _, v := range s {
fmt.Println(v)
}
}
代码有一个 Print
函数,它打印出一个片断的每个元素,其中片断的元素类型,这里称为 T,是未知的。
这里引出了一个要做泛型语法设计的点,那就是:T 的泛型类型参数,应该如何定义?
在现有的设计中,分为两个部分:
结合完整的例子如下:
// Print 可以打印任何片断的元素。
// Print 有一个类型参数 T,并有一个单一的(非类型)的 s,它是该类型参数的一个片断。
func Print[T any](s []T) {
// do something...
}
在上述代码中,我们声明了一个函数 Print
,其有一个类型参数 T,类型约束为 any
,表示为任意的类型,作用与 interface{}
一样。他的入参变量 s
是类型 T 的切片。
函数声明完了,在函数调用时,我们需要指定类型参数的类型。如下:
Print[int]([]int{1, 2, 3})
在上述代码中,我们指定了传入的类型参数为 int,并传入了 []int{1, 2, 3}
作为参数。
其他类型,例如 float64:
Print[float64]([]float64{0.1, 0.2, 0.3})
也是类似的声明方式,照着套就好了。
说完类型参数,我们再说说 “约束”。在所有的类型参数中都要指定类型约束,才能叫做完整的泛型。
以下分为两个部分来具体展开讲解:
为了确保调用方能够满足接受方的程序诉求,保证程序中所应用的函数、运算符等特性能够正常运行。
泛型的类型参数,类型约束,相辅相成。
我们看看 Go 官方所提供的例子:
func Stringify[T any](s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String()) // INVALID
}
return ret
}
该方法的实现目的是:任何类型的切片都能转换成对应的字符串切片。但程序逻辑里有一个问题,那就是他的入参 T 是 any
类型,是任意类型都可以传入。
其内部又调用了 String
方法,自然也就会报错,因为只像是 int、float64 等类型,就可能没有实现该方法。
你说要定义有效的类型约束,那像是上面的例子,在泛型中如何实现呢?
要求传入方要有内置方法,就得定义一个 interface
来约束他。
例子如下:
type Stringer interface {
String() string
}
在泛型方法中应用:
func Stringify[T Stringer](s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String())
}
return ret
}
再将 Stringer
类型放到原有的 any
类型处,就可以实现程序所需的诉求了。
如果是多个类型约束。例子如下:
type Stringer interface {
String() string
}
type Plusser interface {
Plus(string) string
}
func ConcatTo[S Stringer, P Plusser](s []S, p []P) []string {
r := make([]string, len(s))
for i, v := range s {
r[i] = p[i].Plus(v.String())
}
return r
}
与常规的入参、出参类型声明一样的规则。
完成了函数约束的定义后,剩下一个要啃的大骨头就是 “运算符” 的约束了。
我们看看 Go 官方的例子:
func Smallest[T any](s []T) T {
r := s[0] // panic if slice is empty
for _, v := range s[1:] {
if v < r { // INVALID
r = v
}
}
return r
}
经过上面的函数例子,我们很快能意识到这个程序根本无法运行成功。
其入参是 any
类型,程序内部是按 slice 类型来获取值,且在内部又进行运算符比较,那如果真是 slice,内部就可能每个值类型都不一样。
如果一个是 slice,一个是 int 类型,又如何进行运算符的值对比?
可能有的同学想到了重载运算符,但...想太多了,Go 语言没有支持的计划。为此做了一个新的设计,那就是允许限制类型参数的类型范围。
语法如下:
InterfaceType = "interface" "{" {(MethodSpec | InterfaceTypeName | ConstraintElem) ";" } "}" .
ConstraintElem = ConstraintTerm { "|" ConstraintTerm } .
ConstraintTerm = ["~"] Type .
例子如下:
type AnyInt interface{ ~int }
上述声明的类型集是 ~int
,也就是所有类型为 int 的类型(如:int、int8、int16、int32、int64)都能够满足这个类型约束的条件。
包括底层类型是 int8 类型的,例如:
type AnyInt8 int8
也就是在该匹配范围内的。
如果希望进一步缩小限定类型,可以结合分隔符来使用,用法为:
type AnyInt interface{
~int8 | ~int64
}
就可以将类型集限定在 int8 和 int64 之中。
基于新的语法,结合新的概念联合和近似元素,可以把程序改造一下,实现在泛型中的运算符的匹配。
类型约束的声明,如下:
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}
应用的程序如下:
func Smallest[T Ordered](s []T) T {
r := s[0] // panics if slice is empty
for _, v := range s[1:] {
if v < r {
r = v
}
}
return r
}
确保了值均为基础数据类型后,程序就可以正常运行了。
程序员写代码,一定程度的偷懒是必然的。
在一定的场景下,可以通过类型推导来避免明确地写出一些或所有的类型参数,编译器会进行自动识别。
建议复杂函数和参数能明确是最好的,否则读代码的同学会比较麻烦,可读性和可维护性的保证也是工作中重要的一点。
函数例子。如下:
func Map[F, T any](s []F, f func(F) T) []T { ... }
公共代码片段。如下:
var s []int
f := func(i int) int64 { return int64(i) }
var r []int64
明确指定两个类型参数。如下:
r = Map[int, int64](s, f)
只指定第一个类型参数,变量 f 被推断出来。如下:
r = Map[int](s, f)
不指定任何类型参数,让两者都被推断出来。如下:
r = Map(s, f)
神奇的在于,类型推导不仅限与此,连约束都可以推导。
函数例子,如下:
func Double[E constraints.Number](s []E) []E {
r := make([]E, len(s))
for i, v := range s {
r[i] = v + v
}
return r
}
基于此的推导案例,如下:
type MySlice []int
var V1 = Double(MySlice{1})
MySlice 是一个 int 的切片类型别名。变量 V1 的类型编译器推导后 []int 类型,并不是 MySlice。
原因在于编译器在比较两者的类型时,会将 MySlice 类型识别为 []int,也就是 int 类型。
要实现 “正确” 的推导,需要如下定义:
type SC[E any] interface {
[]E
}
func DoubleDefined[S SC[E], E constraints.Number](s S) S {
r := make(S, len(s))
for i, v := range s {
r[i] = v + v
}
return r
}
基于此的推导案例。如下:
var V2 = DoubleDefined[MySlice, int](MySlice{1})
只要定义显式类型参数,就可以获得正确的类型,变量 V2 的类型会是 MySlice。
那如果不声明约束呢?如下:
var V3 = DoubleDefined(MySlice{1})
编译器通过函数参数进行推导,也可以明确变量 V3 类型是 MySlice。
今天我们在文章中给大家介绍了泛型的三个重要概念,分别是:
在内容中也涉及到了联合元素、近似元素、函数约束、运算符约束等新概念。本质上都是基于三个大概念延伸出来的新解决方法,一环扣一环。
你学会 Go 泛型了吗,设计的如何,欢迎一起讨论:)
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/I7qysvddG4NDgtYIMMAE3g
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。