[golang] range ループでは、全体のループの開始前に一度だけ評価を行い、コピーを一時変数に代入する

# 目的
下記の二つを理解する。
- range ループでは、全体のループの開始前に一度だけ評価を行い、コピーを一時変数に代入する
- 一時変数は、毎回のループで異なるメモリ領域(アドレス)を使う


# 検証

```
~/praprago$ go version
go version go1.23.2 linux/amd64
```

``` main.go
package main

import "fmt"

// go run .
func main() {
	fmt.Println(" ================================================================= (1)")
	PraFor1()
	fmt.Println(" ================================================================= (2)")
	PraFor2()
	fmt.Println(" ================================================================= (3)")
	PraFor3()
	fmt.Println(" ================================================================= (4)")
	PraFor4()
	fmt.Println(" ================================================================= (5-1)")
	PraFor5_1()
	fmt.Println(" ================================================================= (5-2)")
	PraFor5_2()
	fmt.Println(" ================================================================= (6)")
	// PraFor6() // コンパイルエラー
	fmt.Println(" ================================================================= (7-1)")
	PraFor7_1()
	fmt.Println(" ================================================================= (7-2)")
	// PraFor7_2() // 無限ループにより、signal: killed が出る
	fmt.Println(" ================================================================= (8)")
	PraFor8()
	fmt.Println(" ================================================================= (9)")
	PraFor9()
}

```

``` pra_for.go
package main

import (
	"fmt"
	"time"
)

type MyStruct struct {
	Name string
	Age  int
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (1)
// range を使った for ループでは、一時変数は、for ループ全体の開始前に一度だけ評価され、常にコピーが使われる。
func PraFor1() {
	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)
		v.Age += 100
		fmt.Println("v:", v)
	}

	fmt.Println("AAAAAAAAAA_111222")
	fmt.Println("myStructs:", myStructs)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (2)
func PraFor2() {
	myStructs := []MyStruct{
		{"XXX", 1},
		{"YYY", 2},
		{"ZZZ", 3},
	}
	for i := range myStructs {
		fmt.Println("DDDDDDDDDD_222333")
		fmt.Println("i:", i)
		fmt.Println("myStructs[i]:", myStructs[i])
		myStructs[i].Age += 100
		fmt.Println("myStructs[i]:", myStructs[i])
	}
	fmt.Println("EEEEEEEEEE_111222")
	fmt.Println("myStructs:", myStructs)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (3)
func PraFor3() {
	myStructs := []MyStruct{
		{"AAA", 1},
		{"BBB", 2},
		{"CCC", 3},
	}
	for i := 0; i < len(myStructs); i++ {
		fmt.Println("FFFFFFFFF_222333")
		fmt.Println("i:", i)
		fmt.Println("myStructs[i]:", myStructs[i])
		myStructs[i].Age += 100
		fmt.Println("myStructs[i]:", myStructs[i])
	}
	fmt.Println("GGGGGGGGGG_111222")
	fmt.Println("myStructs:", myStructs)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (4)
func PraFor4() {
	myStructs := []*MyStruct{
		{"AAA", 1},
		{"BBB", 2},
		{"CCC", 3},
	}
	for i, v := range myStructs {
		fmt.Println("HHHHHHHHHH_222333")
		fmt.Println("i:", i)
		fmt.Printf("v はアドレスでありそのまま出力: %p\n", v)
		fmt.Println("v はアドレスだが勝手に分かりやすく出力される:", v)
		fmt.Println("*v:", *v)
		v.Age += 100
		fmt.Printf("v はアドレスでありそのまま出力: %p\n", v)
		fmt.Println("v はアドレスだが勝手に分かりやすく出力される:", v)
		fmt.Println("*v:", *v)
	}
	fmt.Println("IIIIIIIIII_111222")
	fmt.Println("myStructs:", myStructs)
	fmt.Printf("myStructs[0] はアドレスでありそのまま出力: %p\n", myStructs[0])
	fmt.Printf("myStructs[0] はアドレスだが勝手に分かりやすく出力される: %v\n", myStructs[0])
	fmt.Println("*myStructs[0]:", *myStructs[0])
	fmt.Printf("myStructs[1] はアドレスでありそのまま出力: %p\n", myStructs[1])
	fmt.Printf("myStructs[1] はアドレスだが勝手に分かりやすく出力される: %v\n", myStructs[1])
	fmt.Println("*myStructs[1]:", *myStructs[1])
	fmt.Printf("myStructs[2] はアドレスでありそのまま出力: %p\n", myStructs[2])
	fmt.Printf("myStructs[2] はアドレスだが勝手に分かりやすく出力される: %v\n", myStructs[2])
	fmt.Println("*myStructs[2]:", *myStructs[2])
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (5-1)
func PraFor5_1() {
	a := [3]int{0, 1, 2}
	for i, v := range a {
		fmt.Println("i:", i)
		a[2] = 10
		fmt.Println("v:", v)
	}
	fmt.Println("a:", a)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (5-2)
// 下記のように配列のアドレスを使うと、「一時変数は、for ループ全体の開始前に一度だけ評価され、常にコピーが使われる」というルールが誤解を生むかもしれない。
// for ループ全体の開始前に一度だけ評価されるのは、アドレスまでであり、実際に使用される一時変数 v は、毎回のループ開始前に評価される。
func PraFor5_2() {
	a := [3]int{0, 1, 2}
	for i, v := range &a {
		fmt.Println("i:", i)
		a[2] = 10
		fmt.Println("v:", v)
	}
	fmt.Println("a:", a)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (6)
// スライスのアドレスを使うことはできないので、コンパイルエラーになる。
//
// func PraFor6() {
// 	myStructs := []MyStruct{
// 		{"AAA", 1},
// 		{"BBB", 2},
// 		{"CCC", 3},
// 	}
// 	for i, v := range &myStructs {
// 		fmt.Println("JJJJJJJJJJ_222333")
// 		fmt.Println("i:", i)
// 		myStructs[2].Age += 100
// 		fmt.Println("v:", v)
// 	}
// 	fmt.Println("KKKKKKKKKK_111222")
// 	fmt.Println("myStructs:", myStructs)
// }

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (7-1)
func PraFor7_1() {
	s := []int{1, 2, 3}
	for range s {
		s = append(s, 100)
	}
	fmt.Println(s)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (7-2)
// len(s) は毎回のループごとに評価されるので、無限ループになる。
func PraFor7_2() {
	s := []int{1, 2, 3}
	for i := 0; i < len(s); i++ {
		s = append(s, 100)
	}
	fmt.Println(s)
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (8)
// range を使うと、for ループ全体の開始前に一度だけ評価され、一時変数には常にコピーが代入される。
//
// https://go.dev/doc/go1.22#:~:text=Changes%20to%20the%20language%C2%B6
// v1.22 以降では、一時変数が毎回異なるアドレスを持つようになったので、下記のように書いても、map のバリューが同じポインタを指すことはない。
func PraFor8() {
	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])
}

// ////////////////////////////////////////////////////
// //////////////////////////////////////////////////// (9)
// range では、一時変数は、for ループ全体の開始前に一度だけ評価され、常にコピーが代入される。
// チャネルでも同様である。
func PraFor9() {
	ch1 := make(chan int, 100)
	go func() {
		for i := 0; i < 1000000; i++ {
			select {
			case ch1 <- i:
				fmt.Println("Sent to ch1:", i)
			default:
				fmt.Println("ch1 is closed, skipping:", i)
			}
			time.Sleep(3000 * time.Millisecond)
		}
	}()

	ch2 := make(chan int, 100)
	go func() {
		for i := 0; i < 6; i++ {
			ch2 <- i + 1000
			fmt.Println("Sent to ch2:", i+1000)
			time.Sleep(1000 * time.Millisecond)
		}
	}()

	ch := ch1
	go func() {
		// range は for ループ全体の開始前に一回だけ評価されるので、次回のループには ch = ch2 が影響を及ぼすことはない。
		for v := range ch {
			fmt.Println("ch has received:", v)
			ch = ch2
		}
	}()

	time.Sleep(20 * time.Second)

	fmt.Println("CCCCCCCCCC_999888 closing ch")
	// 上記の ch = ch2 によって、下記の ch は ch2 である。
	close(ch)

	time.Sleep(30 * time.Second)
}

```
```
~/praprago$ go run .
 ================================================================= (1)
BBBBBBBBBB_222333
i: 0
v: {AAA 1}
v: {AAA 101}
BBBBBBBBBB_222333
i: 1
v: {BBB 2}
v: {BBB 102}
BBBBBBBBBB_222333
i: 2
v: {CCC 3}
v: {CCC 103}
AAAAAAAAAA_111222
myStructs: [{AAA 1} {BBB 2} {CCC 3}]
 ================================================================= (2)
DDDDDDDDDD_222333
i: 0
myStructs[i]: {XXX 1}
myStructs[i]: {XXX 101}
DDDDDDDDDD_222333
i: 1
myStructs[i]: {YYY 2}
myStructs[i]: {YYY 102}
DDDDDDDDDD_222333
i: 2
myStructs[i]: {ZZZ 3}
myStructs[i]: {ZZZ 103}
EEEEEEEEEE_111222
myStructs: [{XXX 101} {YYY 102} {ZZZ 103}]
 ================================================================= (3)
FFFFFFFFF_222333
i: 0
myStructs[i]: {AAA 1}
myStructs[i]: {AAA 101}
FFFFFFFFF_222333
i: 1
myStructs[i]: {BBB 2}
myStructs[i]: {BBB 102}
FFFFFFFFF_222333
i: 2
myStructs[i]: {CCC 3}
myStructs[i]: {CCC 103}
GGGGGGGGGG_111222
myStructs: [{AAA 101} {BBB 102} {CCC 103}]
 ================================================================= (4)
HHHHHHHHHH_222333
i: 0
v はアドレスでありそのまま出力: 0xc000010300
v はアドレスだが勝手に分かりやすく出力される: &{AAA 1}
*v: {AAA 1}
v はアドレスでありそのまま出力: 0xc000010300
v はアドレスだが勝手に分かりやすく出力される: &{AAA 101}
*v: {AAA 101}
HHHHHHHHHH_222333
i: 1
v はアドレスでありそのまま出力: 0xc000010318
v はアドレスだが勝手に分かりやすく出力される: &{BBB 2}
*v: {BBB 2}
v はアドレスでありそのまま出力: 0xc000010318
v はアドレスだが勝手に分かりやすく出力される: &{BBB 102}
*v: {BBB 102}
HHHHHHHHHH_222333
i: 2
v はアドレスでありそのまま出力: 0xc000010330
v はアドレスだが勝手に分かりやすく出力される: &{CCC 3}
*v: {CCC 3}
v はアドレスでありそのまま出力: 0xc000010330
v はアドレスだが勝手に分かりやすく出力される: &{CCC 103}
*v: {CCC 103}
IIIIIIIIII_111222
myStructs: [0xc000010300 0xc000010318 0xc000010330]
myStructs[0] はアドレスでありそのまま出力: 0xc000010300
myStructs[0] はアドレスだが勝手に分かりやすく出力される: &{AAA 101}
*myStructs[0]: {AAA 101}
myStructs[1] はアドレスでありそのまま出力: 0xc000010318
myStructs[1] はアドレスだが勝手に分かりやすく出力される: &{BBB 102}
*myStructs[1]: {BBB 102}
myStructs[2] はアドレスでありそのまま出力: 0xc000010330
myStructs[2] はアドレスだが勝手に分かりやすく出力される: &{CCC 103}
*myStructs[2]: {CCC 103}
 ================================================================= (5-1)
i: 0
v: 0
i: 1
v: 1
i: 2
v: 2
a: [0 1 10]
 ================================================================= (5-2)
i: 0
v: 0
i: 1
v: 1
i: 2
v: 10
a: [0 1 10]
 ================================================================= (6)
 ================================================================= (7-1)
[1 2 3 100 100 100]
 ================================================================= (7-2)
 ================================================================= (8)
BBBBBBBBBB_222333
i: 0
v: {AAA 1}
&v: 0xc000010528
BBBBBBBBBB_222333
i: 1
v: {BBB 2}
&v: 0xc000010558
BBBBBBBBBB_222333
i: 2
v: {CCC 3}
&v: 0xc000010588
AAAAAAAAAA_111222
store: map[0:0xc000010528 1:0xc000010558 2:0xc000010588]
store[0]: 0xc000010528
*store[1]: {AAA 1}
store[1]: 0xc000010558
*store[1]: {BBB 2}
store[2]: 0xc000010588
*store[2]: {CCC 3}
 ================================================================= (9)
Sent to ch1: 0
ch has received: 0
Sent to ch2: 1000
Sent to ch2: 1001
Sent to ch2: 1002
Sent to ch2: 1003
Sent to ch1: 1
ch has received: 1
Sent to ch2: 1004
Sent to ch2: 1005
Sent to ch1: 2
ch has received: 2
Sent to ch1: 3
ch has received: 3
Sent to ch1: 4
ch has received: 4
Sent to ch1: 5
ch has received: 5
Sent to ch1: 6
ch has received: 6
CCCCCCCCCC_999888 closing ch
Sent to ch1: 7
ch has received: 7
Sent to ch1: 8
ch has received: 8
Sent to ch1: 9
ch has received: 9
Sent to ch1: 10
ch has received: 10
Sent to ch1: 11
ch has received: 11
Sent to ch1: 12
ch has received: 12
Sent to ch1: 13
ch has received: 13
Sent to ch1: 14
ch has received: 14
Sent to ch1: 15
ch has received: 15
Sent to ch1: 16
ch has received: 16
```



# 参考

https://www.amazon.co.jp/-/en/Teiva-Harsanyi/dp/4295017531

https://go.dev/doc/go1.22#:~:text=Changes%20to%20the%20language%C2%B6