for-range 的使用

for-range 的使用

map 遍历是无序的

在 chan 的使用里,我们提到可以使用 for-range 来遍历 chan,这样的优点是,可以不用关心 chan 是否关闭了,如果关闭了,会自动退出循环,for-range 更普遍的使用是遍历 slice 和 map,但 slice 的遍历是有序的,map 是无序的。

1
2
3
4
5
6
func TestMap(t *testing.T) {
	tmpMap := map[int]int{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
	for _, value := range tmpMap {
		t.Logf("value is %d", value)
	}
}

这串代码的输出,并不一定是 1,2,3,4,5;具体可以参考官方博客:https://go.dev/blog/maps

slice 遍历是有序的

如果是 slice 则一定是有序的,比如:

1
2
3
4
5
6
func TestSlice(t *testing.T) {
	tmpArr := []int{1, 2, 3, 4, 5}
	for _, value := range tmpArr {
		t.Logf("value is %d", value)
	}
}

常见问题

在 go 1.22 之前,for-range 或 for 循环中,临时变量只会创建一次,举个例子:

1
2
3
4
5
6
func TestSlice(t *testing.T) {
	tmpArr := []int{1, 2, 3, 4, 5}
	for index, value := range tmpArr {
		t.Logf("index is %d value is %d", index, value)
	}
}

这里,第 6 行的 index、value 均只创建 1 次,如果使用其地址,则会出错,在 go 1.22 中修复了该问题;
除了使用指针会出错,如果新起协程也会有问题,因为 index、value 只创建一次,举个例子:

1
2
3
4
5
6
7
8
func TestSlice(t *testing.T) {
	tmpArr := []int{1, 2, 3, 4, 5}
	for index, value := range tmpArr {
		go func() {
			t.Logf("index is %d value is %d", index, value)
		}()
	}
}

在 go 1.22 之前,这种写法是有问题的,因为 index、value 只创建 1 次,异步执行时,结果无法预料;
在 go 1.22 之前,这串代码一般需要这样写:

1
2
3
4
5
6
7
8
func TestSlice(t *testing.T) {
	tmpArr := []int{1, 2, 3, 4, 5}
	for index, value := range tmpArr {
		go func(index, value int) {
			t.Logf("index is %d value is %d", index, value)
		}(index, value)
	}
}

建议,如果是新项目,或者是新人,直接用 go 1.22 版本吧,并忘记这个只创建 1 次的问题。

最后更新于