目的
for ループの一時変数はそれぞれ別のメモリ領域(アドレス)に配置されることを知る。
これは、v1.22 で導入された仕様です。
一応インストール手順
$ sudo rm -rf /usr/local/go
$ wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
$ rm -rf go1.21.0.linux-amd64.tar.gz
$ echo "export PATH=\$PATH:/usr/local/go/bin/:\$HOME/go/bin" >> $HOME/.bashrc
$ source $HOME/.bashrc
~/praprago$ go mod init github.com/XXX/praprago
go.mod
module github.com/XXX/praprago
go 1.21.0
$ go version && go env GOROOT && go env GOPATH
go version go1.21.0 linux/amd64
/usr/local/go
/home/ubuntu/go
検証
(1)
main.go
package main
import "fmt"
// go run .
func main() {
fmt.Println(" ================================================================= (1)")
PraFor1()
}
pra_for.go
package main
import (
"fmt"
)
type MyStruct struct {
Name string
Age int
}
// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (1)
func PraFor1() {
store := make(map[int]*MyStruct)
// var store map[int]*MyStruct // これだと store[i] = &v の部分でエラーになる。
myStructs := []MyStruct{
{"AAA", 1},
{"BBB", 2},
{"CCC", 3},
}
for i, v := range myStructs {
fmt.Println("BBBBBBBBBB_222333")
fmt.Println("i:", i)
fmt.Println("v:", v)
fmt.Printf("&v: %p\n", &v)
store[i] = &v
}
fmt.Println("AAAAAAAAAA_111222")
fmt.Println("store:", store)
fmt.Printf("store[0]: %p\n", store[0])
fmt.Printf("*store[1]: %v\n", *store[0])
fmt.Printf("store[1]: %p\n", store[1])
fmt.Printf("*store[1]: %v\n", *store[1])
fmt.Printf("store[2]: %p\n", store[2])
fmt.Printf("*store[2]: %v\n", *store[2])
}
v1.22 以前(検証環境は V1.21.0)
~/praprago$ go run .
================================================================= (1)
BBBBBBBBBB_222333
i: 0
v: {AAA 1}
&v: 0xc000010018
BBBBBBBBBB_222333
i: 1
v: {BBB 2}
&v: 0xc000010018
BBBBBBBBBB_222333
i: 2
v: {CCC 3}
&v: 0xc000010018
AAAAAAAAAA_111222
store: map[0:0xc000010018 1:0xc000010018 2:0xc000010018]
store[0]: 0xc000010018
*store[1]: {CCC 3}
store[1]: 0xc000010018
*store[1]: {CCC 3}
store[2]: 0xc000010018
*store[2]: {CCC 3}
v1.22 以降(検証環境は V1.23.2 )
~/praprago$ go run .
================================================================= (1)
BBBBBBBBBB_222333
i: 0
v: {AAA 1}
&v: 0xc000010018
BBBBBBBBBB_222333
i: 1
v: {BBB 2}
&v: 0xc000010048
BBBBBBBBBB_222333
i: 2
v: {CCC 3}
&v: 0xc000010078
AAAAAAAAAA_111222
store: map[0:0xc000010018 1:0xc000010048 2:0xc000010078]
store[0]: 0xc000010018
*store[1]: {AAA 1}
store[1]: 0xc000010048
*store[1]: {BBB 2}
store[2]: 0xc000010078
*store[2]: {CCC 3}
(2)
~/praprago$ tree -La 3 -I .git
.
├── go.mod
└── main.go
1 directory, 2 files
main.go
package main
import "fmt"
func main() {
numGoroutines := 5
done := make(chan bool, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func() {
fmt.Printf("goroutine %d is running\n", i)
done <- true
}()
}
for i := 0; i < numGoroutines; i++ {
<-done
}
fmt.Println("All goroutines finished")
}
v1.22 以前(検証環境は V1.21.0)
それぞれのループの i
はアドレスが同じなので、仮に、i = 1
で呼ばれている groutine でも、実際にi
を参照するタイミングで、次のループに進んでi
が増えていたら、その瞬間の i
を使うことになる。
下記を見ると、いずれのgroroutine の開始前に全てのループが終了し、全ての goroutine はループ終了後の i
を使うことになる。
~/praprago$ go run main.go
goroutine 5 is running
goroutine 5 is running
goroutine 5 is running
goroutine 5 is running
goroutine 5 is running
All goroutines finished
v1.22 以降(検証環境は V1.23.2 )
~/praprago$ go run main.go
goroutine 4 is running
goroutine 0 is running
goroutine 1 is running
goroutine 2 is running
goroutine 3 is running
All goroutines finished
参考