• 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
    ", 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的值也是对对应元素拷贝,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以值的地址总是相同的,导致结果不如预期。

  • 相关阅读:
    2019.1.8兔子问题和汉诺塔问题的解决代码
    REST
    存储过程和函数练习
    十六、性能优化
    十五、MySQl日志
    Shell入门
    十四、数据备份
    十三、MySQL触发器
    十二、视图
    十一、MySQL锁
  • 原文地址:https://www.cnblogs.com/juanmaofeifei/p/13474469.html
Copyright © 2020-2023  润新知