这是Go泛型系列文章。
其它Go泛型文章:
如果你一直关注Go泛型的设计和实现,一定知道Go泛型代码实现是通过类型参数(type parameter)实现的,当运行泛型代码时,类型参数(type parameter)由类型参数(type argument)替代。(很遗憾parameter和argument都被翻译成了中文参数)
类型参数(type parameter)也有类型,也就是描述这个参数类型行为的元数据,被成为约束(constraint)。最通用的约束就是内建的any
类型,它代表任意的类型:
|
|
在Go泛型设计中, 约束是通过接口类型来实现的(interface
)。因为接口类型和约束的功能黑类似,就是限定type argument必须实现type parameter的约束(方法集)。当然,为了实现泛型的功能,除了方法集之外,Go还对用来当做约束的接口做了扩展,定义了类型集(type set
)的概念,比如下面是约束代表一个type argument可以是int、int8、int16、int32或int64的类型,是并(union
)的关系,所以使用|
符号。
|
|
更进一步,Go还定义了~
的符号,代表只要底层类型都是某个特定类型就可以,所以上面的例子可以写的更通用一些:
|
|
这样type MyInt int
定义的MyInt
类型的实例也满足这个约束。
constraints 包
Go目前的实现新增加一个package,叫做constraints
,用来定义内建的约束,比如常见的:
|
|
甚至 Russ Cox、Ian Lance Taylor他们 提议和讨论为 slice、map、chan增加必要的约束,因为它们太常用了,标准库中都可以用到。(#47203、#47319、47330#)。
|
|
Rob Pike 最近新提交了一个issue,建议在Go 1.18中不要对标准库增加泛型的支持#48918。离Go 1.18发布就四个月了,很多实现还在摸索之中,这是大师给出的一个很中肯的建议,建议相关的哭的改动先增加到扩展库中(
x/exp
),成熟后再加到标准库中,得到了很多Gopher的赞同。这是另外一个话题了。
包constraints
定义常用的约束可以很好的帮助我们开发,但是你有没有感觉有点异常?
省略接口
是的,依照Go泛型规范,我们必须定义一个约束,然后才能在泛型类型和泛型方法中使用,和其它语言的泛型定义相比,你有没有觉得这一点有脱裤子放屁多此一举的味道?
你看上面的Slice、Map、Chan的定义,是不是很冗余?为什么我们不能直接在泛型类型和方法的定义中直接使用~[]Elem
、~map[Key]Val
、~chan Elem
呢?
因此fzipp提议,对于一个非接口的类型,默认等价为一个约束#48424,下面的公式很好的描述了这个功能:
|
|
在泛型的定义中,非接口类型nonInterfaceType
等价于约束interface{~nonInterfaceType}
, 比如~int
等价于interface{~int}
。这样我们就可以省略constraints
包了。 这个提议北接收了,而且相关功能也加入到了go master分支中。
mattn的Go泛型例子中,将一个整形数组转换成一个chan的例子(我稍微改动成更地道的Go的写法):
|
|
这里使用的是constraints.Chan[E]
代表一个泛型的channel,现在可以用更简便的方法了:
|
|
直接使用chan E
就可以了,方不方便?
chan E
隐式地代表interface {chan E}
,使用起来更简捷,不需要额外的接口(约束)定义。
虽然Go 1.18的临近,感觉Go泛型的开发工作越来越重,甚至有一些还不明确的地方,祝福一下吧,希望它顺顺利利的推出。