Go1.23 New Features
Go1.23が8月13日にリリースされ、リリースノートや概要のブログが公開されました。
前回の New Features から1回分空いてしまいましたが、改めまして、この記事では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(イテレータ関数)と呼ばれています。
イテレータ関数が導入されて何が嬉しいか、これだけだとよく分からないかと思いますが、その導入背景や基本的な使い方、応用方法、使う上での注意点については、公式ブログのほか、すでに多くの方がまとめてくださっているので、そちらをご覧ください。
型エイリアスに型パラメータが使える対応が実験的にサポートされました。環境変数にGOEXPERIMENT=aliastypeparams
をつけて実行することで利用可能となります。
type A[T any] struct{ x T }
type B[T any] = A[T] // <= これが可能になる
実験的サポートということもあり、パッケージを跨いでの参照はまだできないことにご注意ください。
Tools
telemetry
Goチームが管理する各種ツールチェーンの利用状況に関する統計情報を収集する仕組みとしてGo Telemetryが導入されました。
デフォルトの設定では、統計情報自体は収集されますが、プライバシへの配慮もあり、ローカルにのみ保存されるようになっています。go telemetry
コマンド[1]を実行することで、いつでも収集した統計情報をGoチームが管理するサーバにアップロード可能な状態にしたり、収集自体を止めることもできます。
収集された情報は以下のページから誰でも閲覧することができます。
リリースノートには載っていませんが、Go Telemetryは環境変数でも制御することができます。
go command
go env
に新しく追加されたフラグ -changed
によって、-w
フラグ経由で設定されていないGoの環境変数の一覧を出力できるようになりました。
go mod tidy
によって更新される差分をdry runで把握できるように -diff
フラグが導入されました。差分がない場合に非ゼロのexit codeを返す点に注意してください。
go.mod
と go.work
の新しいディレクティブとして godebug
が追加されました。これは文字通りGODEBUGを設定できる新しい手段となります。導入背景や実行時にどのように利用されるかは、GODEBUGに関するドキュメントが更新されているので、そちらをご覧下さい。
vet
go vet
のサブコマンドとして stdversion
が追加されました。これは、検証対象のコード内で、そのコードが実行されるGoのバージョンではまだ導入されていない機能を利用している(シンボルを参照している)ことを検知できる機能です。例えば、ビルド制約 //go:build
で go1.21
を指定しているファイル内で Go1.22 で追加された net/netip
パッケージのメソッド AddPort.Compare
を呼んでいる場合を検知できます。
compiler
PGO(Profile Guided Optimization)のビルド時間が大幅に減り、Go1.22までは通常のビルドの2倍以上時間がかかる場合がありましたが、Go1.23からは数パーセントのオーバーヘッドに抑えられる見込みです。
マージ可能なスタックフレームスロットをマージすることで、スタックで使用されるメモリ容量が削減されています。
linker
//go:linkname
ディレクティブを使用して、標準パッケージ内の //go:linkname
ディレクティブがついていないシンボルへの参照が禁止されました。リンカのフラグ -checklinkname=0
を使用してこの挙動を無効化することができます。詳しくはアンドパッドさんの以下の記事にまとまっています。
new packages
unique
同値な値を使いまわしてメモリ使用量を削減したり、同値な値同士の比較を高速に行えるようにするために 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」と呼ばれています。
structs
struct にブランクフィールドとして差し込むことで、メモリレイアウト等を制御する structs
パッケージが追加されました。
minor changes to packages
database/sql
database/sql/driver
パッケージのインターフェース Valuer
のメソッド Value
が返す error
が database/sql
内部で発生する error
をラップし、errors.Is
等でエラーハンドリングしやすくなりました。
encoding/binary
Read
, Write
メソッドの第1引数がsliceとなったメソッド Encode
, Decode
が新しく追加されました。
また、任意の値のbinery(引数の data
)を引数のbyte sliceに追加するメソッド Append
も追加されています。
math/rand/v2
Go1.22で math/rand
パッケージのv2を作成する際に漏れていた Uint
関数および Rand.Unit
メソッドが追加されました。
構造体 ChaCha8
が io.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
で設定したり、構造体 Dialer
や ListenConfig
のフィールドとして設定する方法があります。
構造体 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
を指定することで、削除しない挙動のままにすることもできます。
エラーによるヘッダ削除後は、Content-Encoding: gzip
等によるオンザフライ圧縮が機能しなくなります。代わりにTransfer-Encoding: gzip
等を使用する必要があります。
net/http/httptest
context.Context
付きでテスト用リクエストを生成するメソッド NewRequestWithContext
が追加されました。
net/netip
IPv4アドレスを保持する Addr
と、それのIPv4 Mapped IPv6アドレスを保持する Addr
を、reflect.DeepEqual, ==, Addr.Compare
の3つの方法で比較を行った際に、比較結果が異なる場合がありましたが、Go1.23からは比較結果が一致するようになりました。
net/url
構造体 URL
のメソッド String
の処理が最適化され、処理速度の向上や、メモリアロケーションの大幅な削減が見込まれます。
os
新しく追加された関数 CopyFS
により、io/fs.FS
をローカルのファイルシステムにコピーできるようになります。
Linuxでは、pidfdをサポートしている場合、プロセス関連の関数内部でPIDではなくpidfdを利用するようになります。
path/filepath
スラッシュ(/
)で区切られたパスをOSが指定するパスに変換する関数 Localize
が追加されました。
各OSがどの区切り文字を指定しているかは以下のコードから確認できます。
runtime/pprof
alloc, mutex, block, threadcreate, goroutineといったプロファイルのスタックの最大の深さが32フレームから128フレームに増えました。
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
Timer
と Ticker
はGo1.22までは、明示的に停止するまでGCの対象とならなかったのですが、Go1.23からはGCの対象となりました。
-
以前までは独立したコマンド(
gotelemetry
)でしたが、Go1.23からはgo
コマンドのサブコマンドに組み込まれています。 ↩︎
Discussion