Go 1.27 リリースノート: ジェネリックメソッドと型システムの強化

Go 1.27 は、型システム、パフォーマンスの最適化、および標準ライブラリの拡張に重点を置いた大幅なアップデートです。目玉となる機能はジェネリックメソッドの導入であり、これによりメソッド宣言がレシーバーとは独立した独自の型パラメータを持つことが可能になります。

ジェネリックメソッド

メソッド宣言において独自の型パラメータを宣言できるようになり、ジェネリックな操作をパッケージレベルの関数ではなくメソッドとして実装できるようになりました。これにより、メソッドがジェネリックコンテナの要素型を変更するような変換が可能になります。

type Box[T any] struct{ v T }

// The method declares its own type parameter U (new in Go 1.27).
func (b Box[T]) Map[U any](f func(T) U) Box[U] {
    return Box[U]{v: f(b.v)}
}

主要な制限: インターフェースは型パラメータ化されたメソッドを宣言できず、ジェネリックメソッドをインターフェースの充足に用いることはできません。ジェネリックメソッドがインターフェース内に配置された場合、コンパイラはエラーをスローします。

ジェネリックメソッドに関するコミュニティの視点

一部の開発者は柔軟性の向上を歓迎していますが、他の開発者は認知負荷と可読性に関する懸念を表明しています。

"(b Box[T]) Map[U any](f func(T) U) Box[U] is the type of cognitive weight I was happy that Go avoided."

これに対し、Goメンテナーの @neild は、ジェネリックメソッドが言語内の不整合(関数はジェネリックになれるがメソッドはなれないという点)を解消し、APIを簡素化することを説明しました。例えば、math/rand/v2 における新しい (*Rand).N メソッドは、ユーザーが煩雑な型キャストなしに、特定の *Rand ソースから任意の整数または duration 型の有界な乱数を取得できるようにします。

標準ライブラリの拡張

UUID パッケージ

Go には、RFC 9562 に基づいて UUID を生成および解析するためのトップレベルの uuid パッケージが新たに含まれます。汎用的な uuid.New()、純粋な乱数による UUID を生成する uuid.NewV4()、およびデータベースのキーとして特に有益な時間順序の UUID を生成する uuid.NewV7() が提供されます。

JSON v2 の正式採用

encoding/json/v2 および encoding/json/jsontext は実験的ステータスを卒業し、GOEXPERIMENT=jsonv2 フラグなしで使用可能になりました。注目すべき点は、従来の encoding/json (v1) が、現在は v2 実装によってバックエンドとしてサポートされていることです。この変更は透過的ですが、パフォーマンスの理由から v2 はデフォルトでマップのキーをソートしません。安定した出力を復元するには json.Deterministic オプションを使用できます。

耐量子計算機暗号

新しい crypto/mldsa パッケージは、耐量子デジタル署名スキームである ML-DSA (FIPS 204) を実装しています。これは crypto/x509 および crypto/tls (TLS 1.3) に統合されています。

パフォーマンスとランタイムの改善

メモリ割り当ての高速化

コンパイラは、小さな割り当て(80バイト未満)に対して、サイズに特化したメモリ割り当てルーチンを使用するようになり、それらの特定の割り当てにおけるコストを最大 30% 削減します。これは、通常、バイナリサイズが約 60 KB 増加する代わりに、割り当てが頻繁に行われるプログラムにおいて全体的なパフォーマンスが約 1% 向上することをも結果としてします。

ポータブル SIMD

実験的な simd パッケージにより、ベクトルサイズに依存しない SIMD 指令を有効にします。これは、利用可能な場合はハードウェアのベクトル指令にコンパイルされ、それ以外の場合は純粋な Go のエミュレーションにフォールバックします。これは GOEXPERIMENT=simd を介して有効化されます。Go ランタイムは、すでに Swiss Table マップの実装においてこれを利用しています。

Goroutine のリーク・プロファイリング

以前は実験的であった goroutine リーク検出器は、現在は通常のプロファイルです。runtime/pprof は、 goroutineleak プロファイルを使用して、チャネルやミューテックスによって永久にブロックされている goroutine を特定します。

ツールと開発効率の向上

テストの強化

  • synctest.Sleep: time.Sleepsynctest.Wait を組み合わせ、合成的な時計を進め、 goroutine が落ち着くのを一回のコールで待機します。
  • インメモリ・テスト・サーバー: httptest.NewTestServer は、現在インメモリの擬似ネットワークをサポートしており、実際の TCP ポートをなるべく用いることなく、 t.Cleanup と統合して自動的なリソース管理を実現しています。

言語の使いやすさ(Ergonomics)

  • 構造体リテラルにおけるフィールド・セレクター: 埋め込まれた構造体のプロモートされたフィールドは、構造体リテラルで直接設定できるようになりました(例: User{ID: 7}User{Base: Base{ID: 7}} ではなく)。
  • 汎用的な関数型推論: 推論は変換(conversions)および複合リテラル(composite literals)にも適用されるようになり、スライスやマップにおけるジェネリック関数の使用時に、手動で型引数を指定する必要性が減少しました。
  • strings.CutLastbytes.CutLast: 文字列またはバイトスライスを、区切り文字の最後の出現箇所で分割するための新しいヘルパー関数です。

ツールへのアップデート

  • go test は、バージョン間の差異を防ぐために、デフォルトで stdversion vet チェックを実行します。

  • go doc は、バージョン固有のドキュメント( go doc pkg@version )をサポートし、 -ex フラグを使用して実行可能な例を一覧表示できます。

  • go mod tidy は、分散した require ブロックを、2つの標準的なブロック(直接および間接)に統合します。

その他の注目すべき変更

  • HTTP レスポンス・ボディ: http.Response.Body は、接続の再利用を向上させるために、HTTP/1 において Close 時に自身をドレイン(drain)します。一部のコミュニティ・メンバーは、ダウンロードを中止するために早期の Close に依存しているユーザーにとって、これは「リスクのあるサイレントな挙動の変更」であると指摘しています。

  • Time チャネル: time.After, time.NewTimer, および time.NewTicker によって返されるチャネルは、常にアンバッファード(unbuffered)になります。

  • HTTP/2: 内部実装が、単一の巨大なファイルから専用のパッケージ net/http/internal/http2 に移動されました。

  • Unicode 17: 標準ライブラリは Unicode 17 にアップグレードされました。

  • math/big: 明示的な丸めモード( Trunc, Floor, Round, Ceil )を備えた Int.Divide が追加されました。

}

Sources