Goの最新のベータリリースGo 1.18 beta 1は、パラメータ化された型を使ったジェネリックプログラミングのサポートをついに導入した。長い間待ち望まれた機能だ。さらに、テストファジングのサポートも追加されている。これは、プログラムで不正な動作をする入力を見つけて明らかにするために使われる手法である。
ジェネリックは、今年初めにGoの変更提案プロセスに入っていた。2、3年にわたる予備作業があり、ドラフト提案に至った。ドラフト提案によって、現在の言語定義における位置づけがみつかった。現在正式にデビュしたが、Goのジェネリックはまだプレビュー版である。何が機能して、何が機能しないかのフィードバックを収集することを目的としている。
ジェネリックは、Go 1のリリース以来、Goに対する最も重要な変更です。確かに、これまでの変更の中で唯一最大の言語変更です。大規模な新機能では、新たなユーザが新たなバグを発見するのが一般的です。ジェネリックがこのルールの例外になるとは思いません。必ず適切に注意を払って使ってください。
ジェネリックの背景にある考え方は、関数とデータ構造が使用する型の共通要素を表現できるようにすることとして、よく理解されている。Goにはその可能性がなかった。関数に渡される実際のデータの型を抽象化する方法として、インターフェイス型を定義することによって使えるようになる汎用性のようなものを排除する場合はである。
次のスニペットは、整数や浮動小数点数を含むマップで機能する関数を定義する方法を示している。K
とV
は、map
に関連付けられている実際の型を表すために使用される2つの型パラメータである。
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
上記のコードでは、K
型のパラメータがマップキーに使用される。これはcomparable
である必要があるが、V
はマップが保持する値のタイプであり、int64
あるいはfloat64
のいずれかに制限される。関数を呼び出すとき、明示的に型パラメータを指定するか、可能な場合は渡された引数からコンパイラにそれらを推測させるオプションがある。
ints := map[string]int64{
"first": 34,
"second": 12,
}
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
SumIntsOrFloats[string, int64](ints)
SumIntsOrFloats(floats)
ジェネリックをインターフェースと組み合わせると、より簡単に再利用できる型制約の定義ができるようになる。たとえば、制約int64 | float64
は、次のインターフェイスを使ってNumber
として書き換えることができる。
type Number interface {
int64 | float64
}
ジェネリックはGo 1.18で唯一となる最も重要な新機能である。一方で言及に値するのはジェネリックだけではない。特に、Go 1.18では、ファジングベースのテストと新しいGoワークスペースモードのベータサポートも導入されている。それによって、複数のモジュールを同時に動作させるのが簡単になっている。たとえば、Goワークスペースモードを使うと、特定のインターフェイスを提供するモジュールを、それを使用している他のモジュールと一緒に変更できる。そのため、1つのモジュールのみが編集可能なmainモジュールで、他のモジュールはモジュールキャッシュからロードされるといった制限を回避できる。
Go 1.18ベータ1は、macOS、Linux、Windowsなどの多くのプラットフォームで利用できる。