Go语言的坑: Range

Go语言的坑: Range

[toc]

copy 的问题

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

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

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

[{100} {200} {300}]

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

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

range slice 的话也会 copy 一份:

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

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

指针问题

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

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":{"ID":"3","Balance":0},"2":{"ID":"3","Balance":0},"3":{"ID":"3","Balance":0}}

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

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

输出:

0x1400000e240
0x1400000e2400x1400000e240

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

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

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

或者:

for i := range test {
    current := &test[i] // 使用局部变量
    fmt.Printf("%p\n", current)  
}
Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计