Open1

Goのジェネリクス導入の背景について

Reo UeharaReo Uehara

Go のジェネリクス導入の背景

Go2としてエラーハンドリングと一緒に提案されたみたい
issue だと 2016 年が最初
https://github.com/golang/go/issues/15292
公式の記事は 2018 年
https://go.googlesource.com/proposal/+/master/design/go2draft.md
https://go.googlesource.com/proposal/+/master/design/go2draft-generics-overview.md
ここに詳しくあるらしい
https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/view?tab=t.0
2022年の 1.18 でリリース
https://tip.golang.org/doc/go1.18

  • モチベーションは型の詳細を抽象化したいこと
    • []byte と string の両方を操作できるような関数を用意すること
    • C++, Java の教訓を活かしたかったため、導入に慎重だった
      • コンパイラが明確なエラーを返すようにするとか

Goのジェネリクスにおいて、どのようにインスタンス化されるのか

mattn さんの記事を参考にした
https://zenn.dev/mattn/books/4c7de85ec42cb44cf285

  • Java型は実装は1つ
  • C++型はパラメータとして渡された型の分だけ実装
    • GoはC++型
    • ジェネリック型のリフレクション情報に、コンパイル時の型情報が含まれている
    • 共変、反変はない

C++のジェネリクスのコンパイル

コンパイラはテンプレートのコードに型を適用し、具体的な型に対する専用のコードを生成

template<typename T>
void print(T x) { std::cout << x << std::endl; }
int main() {
    print<int>(42); // テンプレートを int でインスタンス化
    print<double>(3.14); // テンプレートを double でインスタンス化
}

Goのジェネリクスのコンパイル

コンパイラはインスタンス化時に型パラメータを具体的な型に置き換え、型チェックを行う。
C++同様、型情報はコンパイル時に解決される。生成されるコードは異なる。

func Print[T any](s []T) {
    for _, v := range s {
        fmt.Println(v)
    }
}
func main() {
    Print([]int{1, 2, 3}) // T を int に推論・インスタンス化
}

インタフェースとの違い

https://docs.google.com/presentation/d/10YX-P5wChDmBRXqUDDWNThtBXvg81EdS4vhUS_iRtdk/edit?slide=id.ge6d49a2d1e_0_476#slide=id.ge6d49a2d1e_0_476