현재 3권의 신간들인 Go Optimizations 101, Go Details & Tips 101Go Generics 101이 출간되어 있습니다. Leanpub 서점에서 번들을 모두 구입하시는 방법이 비용 대비 효율이 가장 좋습니다.

Go에 대한 많은 정보들과 Go 101 책들의 최신 소식을 얻으시려면 Go 101 트위터 계정인 @Go100and1을 팔로잉 해주세요.

The Status Quo of Go Custom Generics

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.

Type declarations inside generic functions are not currently supported

Currently (Go 1.20), local type declarations are not allowed in generic functions. For example, in the following code, the ordinary function f compiles okay, but the generic function g doesn't.

func f() {
	type _ int // okay
}

func g[T any]() {
	type _ int // error
}

type T[_ any] struct{}

func (T[_]) m() {
	type _ int // error
}

This restriction will be removed from Go 1.20.

Generic type aliases are not supported currently

Currently (Go 1.20), 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 might be supported in future Go versions.

Embedding type parameters is not allowed now

Due to design and implementation complexities, currently (Go 1.20), 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 method set of a constraint is not calculated completely for some cases

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.20), 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.20).

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 toochain versions.

No ways to specific a field set for a constraint

We know that an interface type may specify a method set. But up to now (Go 1.20), 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.

No ways to use common fields for a constraint if the constraint has not a core (struct) type

Currently (Go 1.20), 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.

Fields of values of type parameters are not accessible

Currently (Go 1.20), 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.

Type switches on values of type parameters are not supported now

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.20), 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.

Generic methods are not supported

Currently (Go 1.20), 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.

There are no ways to construct a constraint which allows assignments involving types of unspecific underlying types

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
}

There are no ways to construct a constraint which allows conversion involving types of unspecific underlying types

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.


Index↡

The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.

주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.

이 책의 디지털 버전은 아래와 같은 곳을 통해서 구매할 수 있습니다.
Go 101의 저자인 Tapir는 2016년 7월부터 Go 101 시리즈 책들을 집필하고 go101.org 웹사이트를 유지 관리하고 있습니다. 새로운 콘텐츠는 책과 웹사이트에 수시로 추가될 예정입니다. Tapir는 인디 게임 개발자이기도 합니다. Tapir의 게임을 플레이하여 Go 101을 지원할 수도 있습니다. (안드로이드와 아이폰/아이패드용):
  • Color Infection (★★★★★), 140개 이상의 단계로 이루어진 물리 기반의 캐주얼 퍼즐 게임
  • Rectangle Pushers (★★★★★), 2가지 모드와 104개 이상의 단계로 이루어진 캐주얼 퍼즐 게임
  • Let's Play With Particles, 세가지 미니 게임이 있는 캐주얼 액션 게임
페이팔을 통한 개인 기부도 환영합니다.

색인: