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)