for .. range中的坑

最近在开发中使用了for range来遍历一个slice,结果在测试的时候遇到了bug,最后定位是错误使用for range造成的,这里记录一下:

func redisSlaveScanBigKeys(slaveClient *redis.Client, bigKeyChan chan *bigKeyInstance, wg *sync.WaitGroup) {
	var cursor uint64 = 0
	for {
		page, cursor_new, error := slaveClient.Scan(cursor, "*", 10000).Result()
		if error != nil {
			fmt.Printf("scan keys error, client is %v, error is %s\n", slaveClient, error.Error())
		}
		cursor = cursor_new
		pipeCount := 0
		piper := slaveClient.Pipeline()
		pageCount := len(page)
		keySlice := make([]*string, 0, 10)
		for _, key := range page {
			pageCount--
			pipeCount++
			piper.Type(key)
		     fmt.Println(key, ":", &key)
		     keySlice = append(keySlice, &key)

			........
		}
		if cursor_new == 0 {
			wg.Done()
			break
		}
	}
}

 

 

上面的代码在测试运行中发现,每一个keyslice中的内容都相同,后来在红色的地方发现每一次for .. range .. 循环中的key值都不同,但是 &key 地址是同一个地址。于是,就想了解一下 for .. range .. 的实现:

对slice for range的实现
  // The loop we generate:
  //   for_temp := range
  //   len_temp := len(for_temp)
  //   for index_temp = 0; index_temp < len_temp; index_temp++ {
  //           value_temp = for_temp[index_temp]
  //           index = index_temp
  //           value = value_temp
  //           original body
  //   }

对 map for range的实现
  // The loop we generate:
  //   var hiter map_iteration_struct
  //   for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
  //           index_temp = *hiter.key
  //           value_temp = *hiter.val
  //           index = index_temp
  //           value = value_temp
  //           original body
  //   }

 

可以看到,对于slice 而言,for idx, value := range slice{}  中value的值是对slice中对应元素的拷贝。针对map而言,for key, value := range map 中 key, value的值也是对对应元素拷贝,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以值的地址总是相同的,导致结果不如预期。

posted @ 2020-09-22 19:15  卷毛狒狒  阅读(433)  评论(0编辑  收藏  举报