0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-12-18

目的

下記の二つを理解する。

  • 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

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?