- 今回のリリースでは ジェネリックメソッド を中心に構造体リテラルと型推論が改善され、標準ライブラリ・ランタイム・ツールチェーン全体に実用的な機能が追加された
- サイズ特化メモリアロケーション により、80バイト未満の一部の割り当てコストを最大30%削減し、割り当て集約型プログラムでは約1%の性能向上と約60KBのバイナリ増加が見込まれる
encoding/json/v2が正式提供され、既存のencoding/jsonも内部的に v2 を使用し、標準の UUID パッケージ と FIPS 204 ベースの ML-DSA 署名が追加されたgoroutineleakプロファイル、traceback の pprof ラベル、仮想時間用synctest.Sleep、インメモリ HTTP テストサーバーにより、診断と並行性テスト が容易になった- タイマーチャネルの非バッファ化、HTTP レスポンスボディの自動消費、
//go:linkname制限の強化は既存コードに影響する可能性があり、Go 1.27 への移行前に互換性チェック が必要
言語と型システム
- Go 1.27 の中核である ジェネリックメソッド は、レシーバとは独立した独自の型パラメータを宣言できる
- 以前はトップレベル関数だけがジェネリックにできたため、型に属する演算もパッケージ関数として書く必要があった
Box[T].Map[U]のように、入力とは異なる要素型を返す演算をメソッドとして定義できる- インターフェースは依然として型パラメータ付きメソッドを宣言できず、ジェネリックメソッドでインターフェースを実装することもできない
- 構造体リテラルのキー には、トップレベルのフィールド名だけでなく有効なフィールドセレクタも使える
- 埋め込み構造体で昇格したフィールドを
User{ID: 7}のように直接初期化できる
- 埋め込み構造体で昇格したフィールドを
- 関数の型推論 が変換と複合リテラルにまで拡張された
[]func([]int) int{first, last}のように、期待される関数型に基づいてジェネリック関数の型引数を推論する- 以前は
first[int]、last[int]のように直接インスタンス化する必要があった
ランタイムと性能
- コンパイラが サイズ特化割り当てルーチン を呼び出し、80バイト未満の一部メモリ割り当てコストを最大30%削減する
- 実際の割り当て集約型プログラムでの全体的な性能向上は約1%と見込まれる
- コード修正は不要だが、ワークロードに関係なくバイナリは約60KB大きくなる
GOEXPERIMENT=nosizespecializedmallocで無効化できるが、このオプションは Go 1.28 で削除予定
- デフォルトで有効化されるコンパイラ最適化が3つ追加された
- 値の中で確実に 0 または 1 であるビットを追跡して冗長な演算を除去する known bits データフローパス
- 反復中に変化しない計算をループ外へ移動するループ不変コード移動
- 条件が合えば
switchをfallthroughまで含めてルックアップテーブルにコンパイルする
- 実験的な
GOEXPERIMENT=mapsplitgroupは、マップグループのメモリレイアウトをKVKVKVKVからKKKKVVVVに変更し、デフォルトでは無効 - リンカは型ディスクリプタと itab を専用の
.go.typeセクションへ移し、typelinksとitablinksを削除するreflect.typelinksがオフセットではなく型を返すようになるため、このシンボルに//go:linknameでアクセスするライブラリは影響を受ける可能性がある
ゴルーチン診断とプロファイリング
go.modが Go 1.27 以上のモジュールでは、traceback のゴルーチンヘッダーにruntime/pprofラベルが表示されるpprof.Doで付与した{request: 42}のようなコンテキストが、クラッシュダンプ、SIGQUIT、runtime.Stackの出力に現れる- 機密性の高いラベルが traceback に露出しないよう、
GODEBUG=tracebacklabels=0を引き続き使用できる
- Go 1.26 の実験機能だった ゴルーチンリーク検出器 が正式な
goroutineleakプロファイルへ昇格した- GC を実行して、チャネルやミューテックスなどで永遠に進行できないゴルーチンを見つけ、スタックを出力する
- サービスでは
/debug/pprof/goroutineleakエンドポイントから収集でき、GOEXPERIMENTは不要
暗号と識別子
- 新しい crypto/mldsa パッケージは、FIPS 204 の 耐量子署名 ML-DSA を実装する
MLDSA44、MLDSA65、MLDSA87の3つのパラメータセットが、鍵・署名サイズとセキュリティレベルのトレードオフを提供するcrypto/x509の鍵・署名とcrypto/tlsの TLS 1.3 署名方式にも ML-DSA が追加された
- 標準ライブラリの新しい uuid パッケージは、RFC 9562 UUID を暗号学的に安全な乱数で生成・解析する
uuid.New()は一般用途に適した方式を選び、NewV4()は純粋な乱数、NewV7()は生成時刻順の UUID を作る- 乱数ベースの UUID は比較可能で、
==を直接使用できる - 時系列順にソートされる UUID v7 はデータベースキーに適している
JSON とデータ処理 API
- encoding/json/v2 と低レベルの
encoding/json/jsontextがGOEXPERIMENT=jsonv2なしで提供される- 既存の
encoding/jsonv1 も内部的に v2 実装 を使用する - 一部のエラーメッセージ文言を除き既存の動作を維持するため、移行は不要
- 互換性の問題があれば
GOEXPERIMENT=nojsonv2で元の v1 実装に戻せる - v2 は性能のためマップキーをデフォルトでソートしないため、安定した出力が必要なテストでは
json.Deterministicを指定する必要がある
- 既存の
strings.CutLastとbytes.CutLastは、最後の区切り文字を基準に値を分割する- 区切り文字がなければ、入力全体、空の後半部、
falseを返す
- 区切り文字がなければ、入力全体、空の後半部、
hash/maphashのジェネリックHasher[T]は、値の ハッシュと等価性の戦略 をまとめて定義する- 等価な値は必ず同じハッシュを持たなければならない
- 比較可能型向けの
ComparableHasher[T]が提供され、大文字小文字を無視する文字列のようなユーザー定義戦略も実装できる
math/big.Int.Divideは、商と余りを同時に計算しながらTrunc、Floor、Round、Ceilのいずれかの明示的な丸め方式を適用する- 丸め方式によって余りも変わるため、金融・数値コードにおいて既存の
QuoとModの 0 方向切り捨ての限界を補う
- 丸め方式によって余りも変わるため、金融・数値コードにおいて既存の
math/rand/v2.(*Rand).Nは、自身の*Randソースから任意の整数または duration 型に対する範囲制限付き乱数を生成する
ポータブル SIMD
- 実験的な simd パッケージは、ベクトルサイズ非依存の SIMD API を提供し、
GOEXPERIMENT=simdで有効化する- 対応環境では実際のハードウェアベクトル命令にコンパイルされ、そうでなければ純粋な Go エミュレーションに置き換えられる
Float32sのような型のレーン数は固定ではなく、マシンごとのハードウェア幅に従う
- 標準ライブラリはすでに内部の
simd/archsimdintrinsics を使用している- Swiss Table マップの
MemHash32、MemHash64、StrHashが新しい SIMD ベースで再実装された
- Swiss Table マップの
決定論的テスト
testing/synctest.Sleepは、仮想時間を進め、すべてのゴルーチンが安定するまで待つ処理を一度に行うsynctestバブル内では実時間が流れないため、2秒待機も即座に完了する- 既存の
time.Sleepとsynctest.Waitの組み合わせによる繰り返しコードを置き換える
httptest.NewTestServerは、実際の TCP ポートではなく インメモリ仮想ネットワーク でサーバーを実行するsrv.Client()がプロセス内部のパイプでハンドラに直接接続されるt.Cleanupにより自動でクリーンアップされるため、defer srv.Close()は不要testing/synctestと組み合わせると、HTTP ラウンドトリップも仮想時間上で高速かつ決定論的にテストできる
ネットワークと互換性の変更
time.After、time.NewTimer、time.NewTickerなどが返すチャネルは、常に 同期式の非バッファチャネル になる- 以前のバッファ動作を復元していた
asynctimerchanGODEBUG 設定は削除された
- 以前のバッファ動作を復元していた
- HTTP/1 では
http.Response.Body.Closeが未読のボディを保守的な上限まで読み取り、接続再利用を助ける- 大きなダウンロードを早期終了するために
Closeに依存していたなら、Transport.DisableKeepAlivesで再利用を無効化する必要がある
- 大きなダウンロードを早期終了するために
- HTTP/2 サーバーは RFC 9218 のクライアント優先度を理解し、高優先度のストリームから処理する
Server.DisableClientPriority = trueで従来のラウンドロビン動作を復元できる
- Windows と macOS の
crypto/x509.SystemCertPoolはSSL_CERT_FILEとSSL_CERT_DIRをサポートする- 変数が設定されている場合、ディスク上のルート証明書を読み込み、プラットフォーム API の代わりに Go の検証器を使う
GODEBUG=x509sslcertoverrideplatform=0で無効化できる
- これまで 12,226 行の生成ファイル
h2_bundle.goに含まれていた HTTP/2 実装が、実際のnet/http/internal/http2パッケージに置き換えられた net/httpには、外部公開前段階のプラグ可能な HTTP/3 フック が追加され、テストスイートの一部が HTTP/3 で実行される- 呼び出し可能な公開 API はまだないが、将来の QUIC ベース
http.Transportのための基盤が含まれている
- 呼び出し可能な公開 API はまだないが、将来の QUIC ベース
ツールチェーン
go testは、go.modに宣言された Go バージョンより新しい標準ライブラリシンボルの使用を見つけるstdversionvet チェック をデフォルトで実行するgo doc pkg@versionで特定モジュールバージョンのドキュメントを参照できるgo doc -exは実行可能なサンプルを一覧表示し、サンプル名を直接指定するとソースを出力するgo fixにatomictypes、embedlit、slicesbackward、unsafefuncsのモダナイズ用アナライザーが追加されたwaitgroupはwaitgroupgoに名前変更され、fmtappendfは削除された
- Go 1.27 以上のモジュールで
go mod tidyは、散在したrequireブロックを直接・間接依存関係用の2ブロックに統合しつつコメントを保持する go tool trace -http=:6060はポートだけを指定した場合 localhost のみにバインドし、外部アクセスにはアドレス指定が必要goコマンドは Bazaar(bzr) バージョン管理システムのサポートを削除したcompile、link、asm、cgo、cover、packは、GCC 形式の レスポンスファイル(@file) をサポートし、コマンドライン長の制限を回避できる
Unicode とセキュリティ境界
- 標準ライブラリの Unicode データが Unicode 15 から Unicode 17 に更新された
- Unicode 16 で追加された文字も、記号・表示可能文字として正しく分類される
os.Root.ReadDirとReaddirを使ってルート外へ脱出できていた問題が修正された- 未承認の
//go:linkname使用はさらに難しくなったlinknamestdディレクティブは、標準ライブラリだけが取り込める linkname を示す- リンカがアセンブリシンボルへの linkname アクセスも検査するため、非公式の内部シンボルに依存するコードは早めのテストが必要
1件のコメント
Lobste.rs のコメント
史上最高のリリースかもしれない。SIMD、uuid、jsonv2 がついに入った
やや話題から外れるが、すべてを文字列として扱い、インターフェースメソッド間の契約や関連性を考慮しないとバグが生まれる好例
Equalは大文字小文字を無視するが、Hashは小文字に変換した値を使う。しかしEqualFoldとToLowerは同じ同値関係を定義していないhttps://github.com/golang/go/issues/52204
"\u017F"(ſ、長い s)と"s"はEqualFoldでは等しいが、それぞれをToLowerした結果は異なる。そのため"s"を入れた後に"ſ"を入れると、ハッシュが異なるため別項目として挿入されるさらに面白いのは、ドキュメントがまさにこのケースを警告している点
https://github.com/golang/go/…
https://github.com/golang/go/…
Anton が止まったところから別の人が引き継いでくれてうれしい
Go は好きだが、リリースごとにこうした機能を追加し続けるには、Go の構文 が十分にエレガントなのか疑問
func (b Box[T]) Map[U any](f func(T) U) Box[U] {一方で Go と Zig の冗長さは、レシーバの定義を探すために別の場所へスクロールする必要がない点でよい。定義部より重要ともいえる 呼び出し箇所 では、どれもほとんど同じに見え、型を明示する必要もない
しばらく Go から離れていたが、jsonv2 には期待している。ただ、デフォルトになるなら、Go が長年守ってきた互換性の約束を破ることにならないのか気になる
encoding/json/v2からインポートする必要があるため、自分で選択する形になる。既存のencoding/jsonは内部的に v2 を使うが従来の動作を保証し、jsonv2 には v1 の例外的な動作を再現するフラグも用意されているので、互換性の約束 は破らない