目的
下記の二つを理解する。
- 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
参考