はじめに
Go 1.24のリリースに伴い、Generics type aliasが利用できるようになりました。
実際に触ってみたく、記事にしました!
スライスのジェネリック型エイリアス
package main
import "fmt"
// スライス型のエイリアス
type List[T any] = []T
func main() {
// int型のList
var intList List[int] = []int{1, 2, 3}
fmt.Println(intList) // 出力: [1 2 3]
// string型のList
var stringList List[string] = []string{"A", "B", "C"}
fmt.Println(stringList) // 出力: [A B C]
}
type List[T any] = []T
はT はジェネリック型パラメータで、「任意の型(any)」を受け取ることを意味します。
実行結果
~/go/go_generics $ go run main.go
[1 2 3]
[A B C]
// Map型のエイリアス
type Dictionary[K comparable, V any] = map[K]V
func main() {
// string → int のマップ
var ageMap Dictionary[string, int] = map[string]int{
"Alice": 30,
"Bob": 25,
}
fmt.Println(ageMap) // 出力: map[Alice:30 Bob:25]
// int → string のマップ
var idMap Dictionary[int, string] = map[int]string{
1: "Alice",
2: "Bob",
}
fmt.Println(idMap) // 出力: map[1:Alice 2:Bob]
}
type Dictionary[K comparable, V any] = map[K]V
Kはマップのキーとなる型で、==や!=で比較できる型に限定されます。
~/go/go_generics $ go run main.go
map[Alice:30 Bob:25]
map[1:Alice 2:Bob]
package main
import "fmt"
// ジェネリック構造体
type Container[T any] struct {
Value T
}
// 型エイリアス
type Box[T any] = Container[T]
func main() {
// BoxはContainerと同じ役割
intBox := Box[int]{Value: 100}
fmt.Println(intBox) // 出力: {100}
stringBox := Box[string]{Value: "Hello"}
fmt.Println(stringBox) // 出力: {Hello}
}
実行結果
~/go/go_generics $ go run main.go
{100}
{Hello}
型に制約を持たせる
main.go
package main
import "fmt"
// 制約: 数値型のみ
type Number interface {
int | float64
}
// 制約付きジェネリック構造体
type Container[T Number] struct {
Value T
}
func main() {
// int型は許可
intBox := Container[int]{Value: 100}
fmt.Println(intBox) // 出力: {100}
// float64型も許可
floatBox := Container[float64]{Value: 99.9}
fmt.Println(floatBox) // 出力: {99.9}
// string型はエラー
stringBox := Container[string]{Value: "Hello"} // コンパイルエラー
}
型の拡張
上記例の場合、Number型しか許容していませんでした。ただ、Stringも対応させたい場合は下記のように実施します。
main
package main
import "fmt"
// 数値型制約
type NumberInterface interface {
int | float64
}
// 文字列型制約
type StringInterface interface {
string
}
// NumberまたはStringを許可
type NumberOrString interface {
NumberInterface | StringInterface
}
// 制約付きジェネリック構造体
type Container[T NumberOrString] struct {
Value T
}
func main() {
// 許可された型
intBox := Container[int]{Value: 100}
fmt.Println(intBox) // 出力: {100}
floatBox := Container[float64]{Value: 99.9}
fmt.Println(floatBox) // 出力: {99.9}
stringBox := Container[string]{Value: "Hello"}
fmt.Println(stringBox) // 出力: {Hello}
// 許可されない型(例: bool)
// boolBox := Container[bool]{Value: true} // コンパイルエラー
}
→確かにStringは許容し、他はエラーが起きていることを確認。
最後に
Genericsを使うことで今まで汎用化できなかった箇所も改善されそうですね!