Go 1.18でGenerics(ジェネリクス)が追加されました。
Genericsを利用すると「型が異なるだけで同じ処理をもつ複数の関数」を「1つの関数」として定義することができます。
ここでは、Generics(ジェネリクス)の基本的な使い方を確認します。
目次
動作確認用コード
( Genericsを使わない )
まず、Genericsを使わない状態のコードです。型ごとに関数を用意しています。
package main
import (
"fmt"
)
func main() {
stringValue := "111"
intValue := 111
boolValue := false
fmt.Printf("sampleFuncString: %#v\n", sampleFuncString(stringValue))
fmt.Printf("sampleFuncInt: %#v\n", sampleFuncInt(intValue))
fmt.Printf("sampleFuncBool: %#v\n", sampleFuncBool(boolValue))
}
func sampleFuncString(x string) string {
return x
}
func sampleFuncInt(x int) int {
return x
}
func sampleFuncBool(x bool) bool {
return x
}
sampleFuncString: "111"
sampleFuncInt: 111
sampleFuncBool: false
Genericsの使い方
comparableで型制約
以下、先述のコードをGenericsを利用して1つの関数で実装してみました。
package main
import (
"fmt"
)
func main() {
stringValue := "111"
intValue := 111
boolValue := false
fmt.Printf("sampleFuncGenerics1: %#v\n", sampleFuncGenerics1(stringValue))
fmt.Printf("sampleFuncGenerics1: %#v\n", sampleFuncGenerics1(intValue))
fmt.Printf("sampleFuncGenerics1: %#v\n", sampleFuncGenerics1(boolValue))
}
func sampleFuncGenerics1[T comparable](x T) T {
return x
}
sampleFuncGenerics1: "111"
sampleFuncGenerics1: 111
sampleFuncGenerics1: false
comparable
は、型パラメータを制約したいときにだけ利用できます。
sampleFuncGenerics1
の引数に指定できる値ですが、 ==, !=演算子
で比較可能な値のみになります。
以下のように、スライスなどは ==演算子
で比較不可能なので利用できません。
複数の型で型制約
型パラメータを string
または int
だけに制約したい場合、以下のように実装できます。
package main
import (
"fmt"
)
func main() {
stringValue := "111"
intValue := 111
fmt.Printf("sampleFuncGenerics2: %#v\n", sampleFuncGenerics2(stringValue))
fmt.Printf("sampleFuncGenerics2: %#v\n", sampleFuncGenerics2(intValue))
}
func sampleFuncGenerics2[T string | int](x T) T {
return x
}
sampleFuncGenerics2: "111"
sampleFuncGenerics2: 111
独自インタフェースで型制約
型パラメータを string
または int
だけに制約したい場合、以下のようにインタフェースを定義して実装することも可能です。
package main
import (
"fmt"
)
func main() {
stringValue := "111"
intValue := 111
fmt.Printf("sampleFuncGenerics3: %#v\n", sampleFuncGenerics3(stringValue))
fmt.Printf("sampleFuncGenerics3: %#v\n", sampleFuncGenerics3(intValue))
}
type SampleType interface {
string | int
}
func sampleFuncGenerics3[T SampleType](x T) T {
return x
}
sampleFuncGenerics3: "111"
sampleFuncGenerics3: 111
Underlying Typeで型制約
( ~ | チルダ )
以下例では [T ~int]
といった形で ~
を利用して型制約しています。
package main
import (
"fmt"
)
type SampleType1 int // underlying typeがint
type SampleType2 int // underlying typeがint
func main() {
intValue1 := 111
intValue2 := SampleType1(222)
intValue3 := SampleType2(333)
fmt.Printf("sampleFuncGenerics4: %#v\n", sampleFuncGenerics4(intValue1))
fmt.Printf("sampleFuncGenerics4: %#v\n", sampleFuncGenerics4(intValue2))
fmt.Printf("sampleFuncGenerics4: %#v\n", sampleFuncGenerics4(intValue3))
}
func sampleFuncGenerics4[T ~int](x T) T {
return x
}
~
を利用することで、Underlying Type(基礎型)
で型制約できます。
上記例では Underlying Typeがintの型のみ許可 しています。