Go1.22から(プレビューはGo1.21から)ループ変数のメモリ共有問題が解消されたことは皆様よくご存知かと思います。 cf. Fixing For Loops in Go 1.22
それではもう1歩踏み込んで、ループ変数がイテレーション毎に異なるインスタンスになるのはどのような時でしょうか?以下2つの出力が異なる理由をどう説明できるでしょうか?
for i := range 3 {
fmt.Print(&i) // [0x14000112018, 0x14000112030, 0x14000112038] // 異なるアドレス
}
for i := range 3 {
print(&i) // [0x1400010af18, 0x1400010af18, 0x1400010af18] // 同じアドレス
}
新しいループとそれを取り巻くツールの実装は、既存コードでバグを生み出さない・パフォーマンスを落とさない工夫がされています。
本セッションでは、loopvarパッケージのコードリーディングを通して、これら変更の背後にある内部動作を解説します。 さらに、デザインドキュメントやコミュニティでの議論、周辺ツールを併せて確認することで、セマンティクス変更への理解をより深めたいと思います。
また、Go1.22から不要になったループ変数のコピーを検出する自作linter copyloopvar をgolangci-lintにコントリビュートした話もお伝えします。 このlinterが何をどのように検知するかを紹介する中で、Goにおける静的解析ツールの作成方法も解説します。 linterの自作やOSS貢献に一歩踏み出す際、本セッションがご参考になれば幸いです。