概要
以下、自分用のメモです。忘れないうちにメモメモ。。。
Go 1.22 で for-each ループのループ変数についての挙動変更が予定されています。
以下のissueですね。
内容としては、現状のGoのfor-eachループではループ変数は同じものを再利用する挙動となっているのを、C#などの他の言語と同じような挙動にするという調整ですね。
途中にC#チームのエンジニアの方も参加して意見を述べてくれているのが、なんかいい感じって思いました。
C#やJavaなどに慣れていた身としては、最初Goのこの挙動には面食らいました(んで慣れてしまった)。
で、1.22を待つこと無く、以下のように環境変数を設定すると1.21で試せるみたいなので、試してみました。
GOEXPERIMENT=loopvar
試してみる
gotipを使います。gotipの使い方については以下を参照ください。
gotipが使える状態になったら
$ mkdir app; cd $_ $ gotip mod init app $ cat go.mod module app go 1.21
ってして、下準備は完了。
for-each ループの挙動を確認できるプログラムをちょっと用意。
package main import "fmt" type Item struct { Num int } func main() { items := make([]Item, 0) items = append(items, Item{1}) items = append(items, Item{2}) items = append(items, Item{3}) results := make([]*Item, 0) for _, v := range items { results = append(results, &v) } fmt.Printf("addr=%p\tvalue=%[1]v\n", results[0]) fmt.Printf("addr=%p\tvalue=%[1]v\n", results[1]) fmt.Printf("addr=%p\tvalue=%[1]v\n", results[2]) }
まずは、そのまま動かしてみましょう。
$ gotip build $ ./app addr=0xc0000120c8 value=&{3} addr=0xc0000120c8 value=&{3} addr=0xc0000120c8 value=&{3}
今まで通りの挙動ですね。ループ内で一時変数に取っていないので、全部同じものになります。
で、実験フラグをオンにして再度ビルドしてみます。
$ GOEXPERIMENT=loopvar gotip build $ ./app addr=0xc0000120c8 value=&{1} addr=0xc0000120e0 value=&{2} addr=0xc0000120e8 value=&{3}
出力が変化しました。ちゃんと一時変数として確保してなくても異なる状態になっています。
ついでに、Taskfile.yml 。
# https://taskfile.dev version: '3' tasks: run: cmds: - task: _run-normal - task: _run-loopvar _run-normal: internal: true cmds: - gotip run main.go _run-loopvar: internal: true cmds: - gotip run main.go env: GOEXPERIMENT: loopvar clean: cmds: - gotip clean
参考情報
LoopvarExperiment · golang/go Wiki · GitHub
cmd/compile: add GOEXPERIMENT=loopvar · Issue #57969 · golang/go · GitHub
proposal: spec: redefine range loop variables in each iteration · Issue #20733 · golang/go · GitHub
Frequently Asked Questions (FAQ) - The Go Programming Language
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。