Go语言的坑: Range

[toc]

copy 的问题

使用 range 的时候如果我们直接修改它返回的数据会不生效,因为返回的数据并不是原始数据:

1
2
3
4
5
6
7
8
9
10
11
12
type account struct {
balance float32
}

accounts := []account{
{balance: 100.},
{balance: 200.},
{balance: 300.},
}
for _, a := range accounts {
a.balance += 1000
}

如果像上面这么做,那么输出的 accounts 是:

1
[{100} {200} {300}]

所以我们想要改变 range 中的数据可以这么做:

1
2
3
for i := range accounts {
accounts[i].balance += 1000
}

range slice 的话也会 copy 一份:

1
2
3
4
s := []int{0, 1, 2}
for range s {
s = append(s, 10)
}

这份代码在 range 的时候会 copy 一份,因此只会调用三次 append 后停止。

指针问题

比方我们想要 range slice 并将返回值存到 map 里面供后面业务使用,类似这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Customer struct {
ID string
Balance float64
}

test := []Customer{
{ID: "1", Balance: 10},
{ID: "2", Balance: -10},
{ID: "3", Balance: 0},
}

var m map[string]*Customer
for _, customer := range test {
m[customer.ID] = &customer
}

但是这样遍历 map 里面存的并不是我们想要的,你会发现存的 value 都是最后一个:

1
{"1":{"ID":"3","Balance":0},"2":{"ID":"3","Balance":0},"3":{"ID":"3","Balance":0}}

这是因为当我们使用 range 遍历 slice 的时候,返回的 customer 变量实际上是一个固定的地址:

1
2
3
for _, customer := range test {
fmt.Printf("%p\n", &customer) //我们想要获取这个指针的时候
}

输出:

1
2
0x1400000e240
0x1400000e2400x1400000e240

这是因为迭代器会把数据都放入到 0x1400000e240 这块空间里面:

所以我们可以这样在 range 里面获取指针:

1
2
3
4
for _, customer := range test {
current := customer // 使用局部变量
fmt.Printf("%p\n", &current) // 这里获取的指针是 range copy 出来元素的指针
}

或者:

1
2
3
4
for i := range test {
current := &test[i] // 使用局部变量
fmt.Printf("%p\n", current)
}