go中的slice坑
在进行项目中遇到了一些关于Go中的切片的坑,记录下来,提醒自己
在进行项目调试的时候,发现第一次请求时,能够拿到全部想要的结果。但是之后的所有请求,有一部分结果为空。但是整个方法在进行测试的时候都是能够通过的。不清楚错误是出在哪里。仔细回查代码,并没有感觉到哪里出现了问题。
在反复检查得都要爆炸的时候,突然感觉到了在一个方法中cipher []string
作为参数传入应该会有问题。有了这个预感,就去验证自己的感觉是否是正确的。在对方法调用查询的时候发现,我是使用了一个全局的切片作为参数传入了该方法。于是做出猜想,普通的数组的传递是值传递,会不会是切片的传递其实是个引用传递。这样就能很好的解释了为什么 ,第一次请求能够拿到数据,之后的请求无法拿到数据:在这个方法中我对切片中的数据有移除的操作。如果是引用传递,那么这次移除操作操作的对象就是我所定义的全局的切片,导致全局的切片中的内容全部移除。之后的操作,全局切片中的内容就是空的。讲道理的,那么第二次极其以后的操作为空也就说的通了。但到底是不是猜测的那样,写了的demo来说明:
func main() {
a := []int{1, 2, 3, 4}
b := [4]int{1, 2, 3, 4}
fmt.Println("origin slice: ", a)
fmt.Println("origin array:", b)
sliceTest(a)
arrayTest(b)
fmt.Println("change slice: ", a)
fmt.Println("change array: ", b)
}
func sliceTest(slice []int) {
slice[0] = 5
}
func arrayTest(array [4]int) {
array[0] = 5
}
结果:
可以发现,通过sliceTest方法的确是修改了原始的a切片中的内容。虽然已经可以确定问题的原因,但是还是去网上验证一下。在segmentfault中的这篇关于Go语言中数组的参数传递问题和写一个程序证明切片是值类型而map是引用类型的回答应该说的比较清楚了。讲道理,go的所有的传递都是值传递,slice是个值类型,但是表现出了引用语义。
问题既然发现了那就要进行解决。在学习go的时候记得go内置了一个copy()函数,用于复制内容。于是我很自然的想到了var
一个切片变量,然后使用copy() 函数把传入的切片中的内容复制到这个新创建的变量中。想法是美好的,现实是残酷的。这样写的结果是:var
出来的变量中的内容是空的。不能理解为什么是这个结果,于是去google了一把。在Why can not I copy a slice with copy in golang?(这个需要梯子)的回答中也讲明了:copy(dst,src)
复制min(len(dst), len(src))
个元素,因为我们是直接使用var
出来的dst
的切片,因此len(dst)
为0,即该方法什么都不做。因此不要使用var
申明一个变量,而是使用make()
函数直接创建一个和src
一样大小的切片:
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)