The previous chapters explain the basic knowledge about Go custom generics. This chapter will list some missing features in the current design and implementation of Go custom generics.
Currently (Go 1.23), a declared type alias may not have type parameters.
For example, in the following code, only the alias declaration for A
is legal,
the other alias declarations are all illegal.
The alias A
is actually an alias to an ordinary type func(int) string
.
type T[X, Y any] func(X) Y
type A = T[int, string] // okay
// generic type cannot be alias
type B[X any] = T[X, string] // error
type C[X, Y, Z any] = T[X, Y] // error
type D[X any] = T[int, string] // error
Generic type aliases will be supported since Go 1.24.
Due to design and implementation complexities, currently (Go 1.23), type parameters are disallowed to be embedded in either interface types or struct types.
For example, the following type declaration is illegal.
type Derived[Base any] struct {
Base // error
x bool
}
Please view this issue for reasons.
The Go specification states:
The method set of an interface type is the intersection of the method sets of each type in the interface's type set.
However, currently (Go toolchain 1.23), only the methods explicitly specified in interface types are calculated into method sets.
For example, in the following code, the method set of the constraint should contain both Foo
and Bar
,
and the code should compile okay, but it doesn't (as of Go toolchain 1.23).
package main
type S struct{}
func (S) Bar() {}
type C interface {
S
Foo()
}
func foobar[T C](v T) {
v.Foo() // okay
v.Bar() // v.Bar undefined
}
func main() {}
This restriction is planed to be removed in future Go toolchain versions.
We know that an interface type may specify a method set. But up to now (Go 1.23), it could not specify a (struct) field set.
There is a proposal for this: https://github.com/golang/go/issues/51259.
The restriction might be lifted from future Go versions.
Currently (Go 1.23), even if all types in the type set of a constraint are structs and they share some common fields, the common fields still could not be used if the structs don't share the identical underlying type.
For example, the generic functions in the following example all fail to compile.
package main
type S1 struct {
X int
}
type S2 struct {
X int `json:X`
}
type S3 struct {
X int
Z bool
}
type S4 struct {
S1
}
func F12[T S1 | S2](v T) {
_ = v.X // error: v.x undefined
}
func F13[T S1 | S3](v T) {
_ = v.X // error: v.x undefined
}
func F14[T S1 | S4](v T) {
_ = v.X // error: v.x undefined
}
func main() {}
There is a proposal to remove this limit. A temporary (quite verbose) workaround is to specify/declare some getter and setter methods for involved constraints and concrete types.
Currently (Go 1.23), even if a type parameter has a core struct type, the fields of the core struct type still may not be accessed through values of the type parameter. For example, the following code doesn't compile.
type S struct{x, y, z int}
func mod[T S](v *T) {
v.x = 1 // error: v.x undefined
}
The restriction mentioned in the last section is actually a special case of the one described in the current section.
The restriction (described in the current section) was added just before Go 1.18 is released. It might be removed since a future Go version.
It has been mentioned that a type parameter is an interface type from semantic view. On the other hand, a type parameter has wave-particle duality. For some situations, it acts as the types in its type set.
Up to now (Go 1.23), values of type parameters may not be asserted. The following two functions both fail to compile.
func tab[T any](x T) {
if n, ok := x.(int); ok { // error
_ = n
}
}
func kol[T any]() {
var x T
switch x.(type) { // error
case int:
case []bool:
default:
}
}
The following modified versions of the above two functions compile okay:
func tab2[T any](x T) {
if n, ok := any(x).(int); ok { // error
_ = n
}
}
func kol2[T any]() {
var x T
switch any(x).(type) { // error
case int:
case []bool:
default:
}
}
There is a proposal to use type switches directly on type parameters, like:
func kol3[T any]() {
switch T {
case int:
case []bool:
default:
}
}
Please subscribe this issue to follow the progress of this problem.
Currently (Go 1.23), for design and implementation difficulties, generic methods (not methods of generic types) are not supported.
For example, the following code are illegal.
import "sync"
type Lock struct {
mu sync.Mutex
}
func (l *Lock) Inc[T ~uint32 | ~uint64](x *T) {
l.Lock()
defer l.Unlock()
*x++
}
How many concrete methods do the Lock
type have?
Infinite! Because there are infinite uint32 and uint64 types.
This brings much difficulties to make the reflect
standard package keep backwards compatibility.
There is an issue for this.
And there are not such predeclared constraints like the following supposed assignableTo
and assignableFrom
constraints.
// This function doesn't compile.
func yex[Tx assignableTo[Ty], Ty assignableFrom[Tx]](x Tx, y Ty) {
y = x
}
And there are not such predeclared constraints like the following supposed convertibleTo
and convertibleFrom
constraints.
// This function doesn't compile.
func x2y[Tx convertibleTo[Ty], Ty convertibleFrom[Tx],
// The second value argument is
// for type inference purpose.
](xs []Tx, _ Ty) []Ty {
if xs == nil {
return nil
}
ys := make([]Ty, len(xs))
for i := range xs {
ys[i] = Ty(xs[i])
}
return ys
}
var bs = []byte{61, 62, 63, 64, 65, 66}
var ss = x2y(bs, "")
var is = x2y(bs, 0)
var fs = x2y(bs, .0)
Currently, there is an ungraceful workaround implementation:
func x2y[Tx any, Ty any](xs []Tx, f func(Tx) Ty) []Ty {
if xs == nil {
return nil
}
ys := make([]Ty, len(xs))
for i := range xs {
ys[i] = f(xs[i])
}
return ys
}
var bs = []byte{61, 62, 63, 64, 65, 66}
var ss = x2y(bs, func(x byte) string {
return string(x)
})
var is = x2y(bs, func(x byte) int {
return int(x)
})
var fs = x2y(bs, func(x byte) float64 {
return float64(x)
})
The workaround needs a callback function, which makes the code verbose and much less efficient, though I do admit it has more usage scenarios.
Go101.org网站内容包括Go编程各种相关知识(比如Go基础、Go优化、Go细节、Go实战、Go测验、Go工具等)。后续将不断有新的内容加入。敬请收藏关注期待。
本丛书微信公众号(联系方式一)名称为"Go 101"。二维码在网站首页。此公众号将时不时地发表一些Go语言相关的原创短文。各位如果感兴趣,可以搜索关注一下。
《Go语言101》系列丛书项目目前托管在Github上(联系方式二)。欢迎各位在此项目中通过提交bug和PR的方式来改进完善《Go语言101》丛书中的各篇文章。我们可以在项目目录下运行go run .
来浏览和确认各种改动。
本书的twitter帐号为@Golang_101(联系方式三)。玩推的Go友可以适当关注。
你或许对本书作者老貘开发的一些App感兴趣。