🆕

Go1.23 New Features

2024/08/27に公開

Go1.23が8月13日にリリースされ、リリースノート概要のブログが公開されました。
前回の New Features から1回分空いてしまいましたが、改めまして、この記事ではGo1.23の中から気になった新機能についてご紹介していきます。

https://go.dev/doc/go1.23

spec

Go1.22で実験的に導入されていたrange-over funcがGo1.23で本格導入されています。Goでループをまわす際はrangeを使いますが、このrangeが受け取れる型として、以下のシグネチャを持つ関数型が追加されました。

func(func() bool)
func(func(K) bool)
func(func(K, V) bool)

これらの関数はiterator functions(イテレータ関数)と呼ばれています。

イテレータ関数が導入されて何が嬉しいか、これだけだとよく分からないかと思いますが、その導入背景や基本的な使い方、応用方法、使う上での注意点については、公式ブログのほか、すでに多くの方がまとめてくださっているので、そちらをご覧ください。

https://go.dev/blog/range-functions
https://docs.google.com/presentation/d/1KBqbbxt2ASQv1g3VlQ8kQgnzlHAdncbkqBD5cYPdKo0/
https://future-architect.github.io/articles/20240718a/
https://zenn.dev/syumai/articles/cqud4gab5gv2qkig5vh0
https://zenn.dev/mattn/articles/641f1d86fffdc9
https://zenn.dev/koron/articles/0a89d036d946a8


型エイリアスに型パラメータが使える対応が実験的にサポートされました。環境変数にGOEXPERIMENT=aliastypeparams をつけて実行することで利用可能となります。

type A[T any] struct{ x T }
type B[T any] = A[T] // <= これが可能になる

実験的サポートということもあり、パッケージを跨いでの参照はまだできないことにご注意ください。

https://github.com/golang/go/issues/46477

Tools

telemetry

Goチームが管理する各種ツールチェーンの利用状況に関する統計情報を収集する仕組みとしてGo Telemetryが導入されました。

https://go.dev/doc/telemetry
https://zenn.dev/ymotongpoo/articles/20231222-gotelemetry

デフォルトの設定では、統計情報自体は収集されますが、プライバシへの配慮もあり、ローカルにのみ保存されるようになっています。go telemetry コマンド[1]を実行することで、いつでも収集した統計情報をGoチームが管理するサーバにアップロード可能な状態にしたり、収集自体を止めることもできます。

収集された情報は以下のページから誰でも閲覧することができます。

https://telemetry.go.dev/

リリースノートには載っていませんが、Go Telemetryは環境変数でも制御することができます。

https://go-review.googlesource.com/c/go/+/607855

go command

go env に新しく追加されたフラグ -changed によって、-w フラグ経由で設定されていないGoの環境変数の一覧を出力できるようになりました。

https://go.dev/issue/34208


go mod tidy によって更新される差分をdry runで把握できるように -diff フラグが導入されました。差分がない場合に非ゼロのexit codeを返す点に注意してください。

https://go.dev/issue/27005


go.modgo.work の新しいディレクティブとして godebug が追加されました。これは文字通りGODEBUGを設定できる新しい手段となります。導入背景や実行時にどのように利用されるかは、GODEBUGに関するドキュメントが更新されているので、そちらをご覧下さい。

https://go.dev/doc/godebug
https://go.dev/issue/65573

vet

go vet のサブコマンドとして stdversion が追加されました。これは、検証対象のコード内で、そのコードが実行されるGoのバージョンではまだ導入されていない機能を利用している(シンボルを参照している)ことを検知できる機能です。例えば、ビルド制約 //go:buildgo1.21 を指定しているファイル内で Go1.22 で追加された net/netip パッケージのメソッド AddPort.Compare を呼んでいる場合を検知できます。

https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdversion
https://go.dev/issue/46136

compiler

PGO(Profile Guided Optimization)のビルド時間が大幅に減り、Go1.22までは通常のビルドの2倍以上時間がかかる場合がありましたが、Go1.23からは数パーセントのオーバーヘッドに抑えられる見込みです。


マージ可能なスタックフレームスロットをマージすることで、スタックで使用されるメモリ容量が削減されています。

https://github.com/golang/go/issues/62737

linker

//go:linkname ディレクティブを使用して、標準パッケージ内の //go:linkname ディレクティブがついていないシンボルへの参照が禁止されました。リンカのフラグ -checklinkname=0 を使用してこの挙動を無効化することができます。詳しくはアンドパッドさんの以下の記事にまとまっています。

https://tech.andpad.co.jp/entry/2024/06/20/140000
https://go.dev/issue/67401

new packages

unique

同値な値を使いまわしてメモリ使用量を削減したり、同値な値同士の比較を高速に行えるようにするために unique パッケージが追加されました。

https://github.com/golang/go/issues/62483
https://pkg.go.dev/unique

公式による解説記事も公開されています。
https://go.dev/blog/unique

iter

イテレータ関数を表現するシグネチャは少々複雑なので、使いやすくするために iter パッケージが追加されました。3つのシグネチャのうち、内側の関数(yield)に引数を持つものがそれぞれ型 Seq, Seq2 として定義されています。

type (
	Seq[V any]     func(yield func(V) bool)
	Seq2[K, V any] func(yield func(K, V) bool)
)

また、Seq, Seq2 は for-range に渡して実行する使い方をするのですが、for-range と切り離してループを実行できるように変換できるメソッド Pull, Pull2 も定義されています。Pull および Pull2 は戻り値として、ループを1回まわして戻り値を受け取る next と、ループを終了させる stop を返します。

なおSeqは、yield に値を渡すので「Push Iterator」と呼ばれているのに対し、Pullはイテレータ関数から値を取り出すので「Pull Iterator」と呼ばれています。

https://pkg.go.dev/iter

structs

struct にブランクフィールドとして差し込むことで、メモリレイアウト等を制御する structs パッケージが追加されました。

https://pkg.go.dev/structs

minor changes to packages

database/sql

database/sql/driver パッケージのインターフェース Valuer のメソッド Value が返す errordatabase/sql 内部で発生する error をラップし、errors.Is 等でエラーハンドリングしやすくなりました。

https://github.com/golang/go/issues/64707

encoding/binary

Read, Write メソッドの第1引数がsliceとなったメソッド Encode, Decode が新しく追加されました。
また、任意の値のbinery(引数の data)を引数のbyte sliceに追加するメソッド Append も追加されています。

math/rand/v2

Go1.22で math/rand パッケージのv2を作成する際に漏れていた Uint 関数および Rand.Unit メソッドが追加されました。


構造体 ChaCha8io.Reader インターフェースを満たすようになりました。

net

TCP Keep-Aliveを細かく制御できる構造体 KeepAliveConfig が追加されました。
制御できるパラメータとしては、以下の4つがあります。

  • Keep-Aliveを有効にするかどうかを決める Enable
  • 最初のKeep-Alive Proveを送るまでの時間を決める Idle
  • Keep-Alive Proveを送る間隔を決めるInterval
  • Keep-Alive Proveを送る最大回数を決める Count

KeepAliveConfig の設定方法としては、構造体 TCPConn のメソッド SetKeepAliveConfig で設定したり、構造体 DialerListenConfig のフィールドとして設定する方法があります。


構造体 DNEError がタイムアウトやキャンセルのエラーをラップするようになり、構造体 context.DeadlineExceeded 等と比較しやすくなりました。

net/http

構造体 Cookie に、Cookieがダブルクオートで囲まれているかどうかを表す Quoted フィールドを保持するようになりました。


構造体 Cookie に、CookieがPartitioned属性を保持しているかを表す Partitioned フィールドが追加されました。


新しく追加されたメソッド ParseCookie は、引数に含まれるCookieヘッダの値をsliceとして返します。


新しく追加されたメソッド ParseSetCookie は、引数に含まれるSet-Cookieヘッダの値を返します。


Go1.22で ServeMux メソッドの機能が強化され、HTTPメソッドとルーティングパスの組み合わせのマルチプレクサを設定できるようになりました。ただ、HTTPメソッドとパスの間には1つの半角スペースしか挟むことができませんでしたが、Go1.23では複数個の半角スペースやタブを挟めるようになっています。

"GET   /health" // <= 半角スペースが何個あっても良くなった

メソッド ServeContent, ServeFile, ServeFileFS は、エラー時にCache-Control, Content-Encoding, Etag, Last-Modifiedヘッダを削除するようになりました。GODEBUG=httpservecontentkeepheaders=1 を指定することで、削除しない挙動のままにすることもできます。

https://go-review.googlesource.com/c/go/+/571995

エラーによるヘッダ削除後は、Content-Encoding: gzip 等によるオンザフライ圧縮が機能しなくなります。代わりにTransfer-Encoding: gzip等を使用する必要があります。

https://github.com/golang/go/issues/66343#issuecomment-2173859571

net/http/httptest

context.Context 付きでテスト用リクエストを生成するメソッド NewRequestWithContext が追加されました。

net/netip

IPv4アドレスを保持する Addr と、それのIPv4 Mapped IPv6アドレスを保持する Addr を、reflect.DeepEqual, ==, Addr.Compare の3つの方法で比較を行った際に、比較結果が異なる場合がありましたが、Go1.23からは比較結果が一致するようになりました。

https://github.com/golang/go/issues/68113

net/url

構造体 URL のメソッド String の処理が最適化され、処理速度の向上や、メモリアロケーションの大幅な削減が見込まれます。

https://github.com/golang/go/commit/a81c8b3bf2697b86ac7d3d29d7c0fb71c0716a84

os

新しく追加された関数 CopyFS により、io/fs.FS をローカルのファイルシステムにコピーできるようになります。


Linuxでは、pidfdをサポートしている場合、プロセス関連の関数内部でPIDではなくpidfdを利用するようになります。

path/filepath

スラッシュ(/)で区切られたパスをOSが指定するパスに変換する関数 Localize が追加されました。
各OSがどの区切り文字を指定しているかは以下のコードから確認できます。
https://cs.opensource.google/go/go/+/refs/tags/go1.23.0:src/internal/filepathlite/path.go

runtime/pprof

alloc, mutex, block, threadcreate, goroutineといったプロファイルのスタックの最大の深さが32フレームから128フレームに増えました。

https://go-review.googlesource.com/c/go/+/572396

runtime/trace

ハンドリングされない panic が発生した際に、トレースデータを即時に出力するようになり、プログラムのクラッシュによるトレースデータの欠損が削減されました。

slices

Go1.18でGenericsが導入されて以来、sliceを便利に扱う関数が slices パッケージに追加されてきましたが、イテレータの仕組みが導入されるまで追加が保留になっていた関数もいくつかありました。そして今回のイテレータ導入により、slices パッケージにはいくつか関数が追加されています。

All

sliceのインデックスと要素のペアを返すイテレータを返します。

Values

sliceの要素を返すイテレータを返します。

Backward

sliceの後方から走査しながらインデックスと要素のペアを返すイテレータを返します。なお、Values に相当する関数は追加されていません。

Collect

イテレータ関数から最後まで値を取り出して得られた要素列をsliceに変換します。sliceの要素数があらかじめ分かっている場合は、後述の関数 AppendSeq を使用したほうが、メモリアロケーションの観点で良いかなと思います。

AppendSeq

第2引数で渡したイテレータ関数から最後まで値を取り出し、第1引数で渡したsliceにその値を追加していきます。

Sorted

イテレータ関数から最後まで値を取り出し、ソートされたsliceに変換します。

SortedFunc

関数 Sorted でソートを行う際に、比較の仕方を指定したい場合はこちらを使います。

SortedStableFunc

関数 SortedFunc で安定ソートを行いたい場合はこちらを使います。

Chunk

sliceの要素を第2引数で指定した数ずつsliceで返すイテレータを返します。

Repeat

イテレータとは直接関係ないですが、新しく slices パッケージに追加された関数なので、おまけで説明します。
第1引数に渡したsliceの要素を第2引数で渡した整数分繰り返したsliceを生成します。なお、第1引数に渡したsliceはこの関数によって破壊的な変更は加えられません。

maps

slices パッケージと同様にイテレータ導入まで保留になっていた関数が maps パッケージに追加されています。

All

mapのkey-valueペアで返すイテレータを返します。

Keys

mapのkeyを返すイテレータを返します。

Values

mapのvalueを返すイテレータを返します。

Insert

第2引数で渡したイテレータ関数から最後まで値(key-valueペア)を取り出し、第1引数で渡したmapにその値を追加していきます。

Collect

イテレータ関数から最後まで値(key-valueペア)を取り出して得られたkey-valueペアの集合をmapに変換します。

sync

構造体 Map に、すべての要素を削除するメソッド Clear が追加されました。

sync/atomic

引数に渡した mask とビット単位でAND演算やOR演算ができるメソッド And, Or が追加されました。またそれらの整数型毎の関数も追加されています。戻り値には演算前の値が返ってきます。

text/template

else 句と with 句 を合わせた else with 句が追加されました。以下のように簡略化して定義が可能となっています。

{{with pipeline}} T1 {{else}}{{with pipeline}} T0 {{end}}{{end}}
=> {{with pipeline}} T1 {{else with pipeline}} T0 {{end}}

time

TimerTicker はGo1.22までは、明示的に停止するまでGCの対象とならなかったのですが、Go1.23からはGCの対象となりました。

脚注
  1. 以前までは独立したコマンド(gotelemetry)でしたが、Go1.23からは go コマンドのサブコマンドに組み込まれています。 ↩︎

Discussion