いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-328 (Go 1.22 で予定されているfor-eachループのループ変数に関する挙動変更を試す)(gotip, GOEXPERIMENT, loopvar)

概要

以下、自分用のメモです。忘れないうちにメモメモ。。。

Go 1.22 で for-each ループのループ変数についての挙動変更が予定されています。

以下のissueですね。

github.com

内容としては、現状のGoのfor-eachループではループ変数は同じものを再利用する挙動となっているのを、C#などの他の言語と同じような挙動にするという調整ですね。

途中にC#チームのエンジニアの方も参加して意見を述べてくれているのが、なんかいい感じって思いました。

C#やJavaなどに慣れていた身としては、最初Goのこの挙動には面食らいました(んで慣れてしまった)。

で、1.22を待つこと無く、以下のように環境変数を設定すると1.21で試せるみたいなので、試してみました。

GOEXPERIMENT=loopvar

試してみる

gotipを使います。gotipの使い方については以下を参照ください。

devlights.hatenablog.com

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のおすすめ書籍

Go言語による並行処理

Go言語による並行処理

Amazon


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。