不积跬步,无以至千里

初学Golang踩过的坑


2018-10-30

append

昨天上线完之后,线上报空指针.修改点如下:

go-init.png

但是在实际使用appInfoList的时候,报空指针,也就是取到的值为空.

把问题简化为以下的代码:

func InitSlice()  {
	slice1:=[]int{1,2,3,4,5,6,7,8,9,0}
	slice2:=make([]*int,len(slice1))
	for _,item:=range slice1{
		slice2= append(slice2, &item)
	}
	fmt.Println(slice2)
}

输出结果为:[ 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0 0xc4200823f0]

所以出问题的地方在于slice的append方法

源码中append的说明:

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//	slice = append(slice, elem1, elem2)
//	slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//	slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

append在当前slice的末尾添加元素,添加测试代码:

func InitSlice()  {
	slice1:=[]int{1,2,3,4,5,6,7,8,9,0}
	slice2:=make([]*int,len(slice1))
	for index,item:=range slice1{
		slice2= append(slice2, &item)
		fmt.Println("size=", len(slice2),"index=",index)
	}
}

输出为:

size= 11 index= 0 size= 12 index= 1 size= 13 index= 2 size= 14 index= 3 size= 15 index= 4 size= 16 index= 5 size= 17 index= 6 size= 18 index= 7 size= 19 index= 8 size= 20 index= 9

所以在确定元素个数的情况下,正确的实现:

func InitSlice()  {
	slice1:=[]int{1,2,3,4,5,6,7,8,9,0}
	slice2:=make([]*int,len(slice1))
	for index,item:=range slice1{
		slice2[index]=&item
		fmt.Println("size=", len(slice2),"index=",index)
	}
}

简单的东西用的多了,就容易忽略它本身的含义.所以一开始还是应该好好打好基础,往往越是简单的东西排查起来越难.

2018-09-13

range

依旧是range


func main() {
	var slice = make([]int64, 4)
	slice[0] = 0
	slice[1] = 1
	slice[2] = 2
	slice[3] = 3
	for index, item := range slice {
		fmt.Println(index, item)
	}
	var dataMap = make(map[int64](string))
	dataMap[1] = "3"
	dataMap[2] = "4"
	dataMap[6] = "2"
	//maps[1] = "d"
	dataMap[8] = "adf"
	dataMap[7] = "adf"
	fmt.Println(dataMap)
	for key, exist := range dataMap {
		fmt.Println(key, exist)
	}
}

通过以上测试代码可以看到:

对于go语言的map,顺序和放入的顺序一致;如果先后两次放入相同的key,则重复的key在整个map的最后

range遍历的时候,对于数组或切片,按照index遍历;对于map,顺序随机

2018-08-15

range

今天被range坑到了,看下边的代码:

func ReApplyService(c *gin.Context, applyId int64) (*common.ReApplyParam, error) {
	params, err := service.GetAllServiceParams(c, &model.ServiceConfig{Name: applyInfo.ApplyServiceName})
	if err == nil {
		if applyInfo.AppId > 0 {
			params, err = service.AddExistParamValues(c, params, applyInfo.AppId)
		}
		if err == nil {
			var applyParam, err = sub_module.GetHandler(applyInfo.ApplyServiceName).ReApply(c, applyInfo.ApplyParams)
			if err == nil {
				if len(*applyParam) > 0 {
					for _,item := range params {
						if params[index].IsModifiable == 1 {
							if (*applyParam)[strconv.Itoa(int(params[index].ID))] == nil {
								item.Value = ""
							} else {
								item.Value = (*applyParam)[strconv.Itoa(int(item.ID))].(string)
							}
						}
					}
				}      
				result.Params = params
				result.ServiceId = int64((data)[0].ID)
				return &result, nil
			}
		}
	}
	logs.Error("reApply error:%v", err)
	return nil, errors.New("get params error")
}

变量params在这段代码中会做三次赋值,一次是通过GetAllServiceParams,第二次是通过AddExistParamValues,第三次则是在for循环中,问题是,第三次赋值一直不成功,打日志后发现item.Value成功赋值,但result.Params是第二次赋值后的结果.最开始印象里记得看过一篇博客,说go会存在局部变量的问题,但似乎并不符合现在的情况.然后看到range,想起来range会创建一个新的切片,导致item的修改并不会影响到params.

这么说起来的话,用range做数组的遍历,会产生一个临时的切片,性能上来说不如直接用index做遍历好一点.

go跟java还是有区别的,不同语言间的切换还是有点不习惯