ジェネリクス(Go 1.18で追加)を使用すると、型パラメータを通じて複数の型に対応する関数と型を記述しながら、コンパイル時の型安全性を維持できます。型ごとにコードを重複させるか、interface{}で安全性を失うかのどちらかという古いトレードオフを排除します。
ジェネリクスが解決する問題
{ ... }
{ ... }
{} { ... }
// T is a type parameter constrained to be "ordered" (comparable with < >)
func Max[T constraints.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
Max(3, 5) // T inferred as int → 5
Max(2.5, 1.1) // T inferred as float64 → 2.5
Max("a", "b") // T inferred as string → "b"
[T constraints.Ordered]は型パラメータTを制約(許可される型)とともに宣言します。コンパイラは引数からTを推論します — 1つの実装が多くの型に対応し、完全に型安全です。
// a constraint is an interface listing allowed types or required methods
type Number interface {
~int | ~int64 | ~float64 // the | union means "any of these"
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n // safe — T is constrained to numeric types
}
return total
}
Sum([]int{1, 2, 3}) // 6
Sum([]float64{1.5, 2.5}) // 4.0
// built-in constraints:
any // any type (alias for interface{})
comparable // types usable with == (for map keys, etc.)
// golang.org/x/exp/constraints: Ordered, Integer, Float, etc.
制約は型パラメータで有効な操作を定義します(~は「このベース型を持つ任意の型」を意味します)。
// a generic stack that works for any type
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) }
func (s *Stack[T]) Pop() T {
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item
}
intStack := Stack[int]{} // a stack of ints
strStack := Stack[string]{} // a stack of strings — same code
✓ Generic data structures (stacks, trees, sets), utility functions over collections
(Map, Filter, Reduce), type-safe containers
✗ Don't overuse — if interfaces or a concrete type work simply, prefer them.
Go's philosophy still favors simplicity; generics are a tool, not a default.
ジェネリクスは Go(1.18)への重要で長年待望されていた追加機能であり、実際の問題点を解決しました。以前は、複数の型に対応する再利用可能なコードを記述する場合、型ごとにコードを重複させるか(冗長で保守不可能)、interface{}を使用するか(コンパイル時の型安全性を失い、実行時アサーションが必要)のどちらかを強いられました。
ジェネリクスを使用すると、型安全で再利用可能な関数とデータ構造(コンテナ、Map/Filter/Reduce などの収集ユーティリティ)を一度記述でき、コンパイラが正確性を強制し、型を推論します。
型パラメータ、制約(any、comparable、union/~構文を含む)、およびジェネリック型の理解は、エコシステムがそれらを採用するにつれてますます重要になります。
とても重要なのは、ジェネリクスはデフォルトではなく、本当の再利用のためのツールであるという Go の考え方です — 過度に使用すると Go のシンプル性の哲学に反します。
これはモダン Go との流暢さを反映する、現在議論されている頻繁なトピックです。