回憶起寫golang時候踩過的坑,那是我逝去的青春……
我們可能會遇到給其他函數傳遞一個slice,讓其他函數給這個slice做一些修改的情況。想到slice是引用傳遞,可以直接傳遞slice用作修改,於是可能出現下面這種情況:
package main
import (
"fmt"
"testing"
)
func TestModifySlice(t *testing.T) {
s := []string{"123"}
modifySlice(s)
fmt.Println(s)
}
func modifySlice(s []string) {
s[0] = "modify:123"
s = append(s, "456")
}
我們利用modifySlice函數對傳入的s進行修改和添加的操作,想到slice是引用傳遞,在modifySlice中的修改必然會生效。
但是實際上呢,我們看一下運行情況:
運行結果表明我們對s[0]做的修改生效了,但是添加新元素的操作卻未生效,這是爲什麼呢?
原因就在於添加新元素是用的append,並將原先的引用重新賦值了!
我們把引用當成指針來看,指針指向的內容就是slice的數據內容。
指針本身是一種變量類型,指針類型在傳遞時,是值傳遞!
也就是說我們在外面調用modifySlice時,傳入s是一個指針變量,當進入到modifySlice函數時,形參s‘跟外面傳入的s並不是一個值,是s的值拷貝!但是他們的值是一樣的,即指向的數據內容都是slice中的數據。
我們用s[0] = ”modify:123“做了修改,實際上是修改指針指向的數據的內容!這裏肯定會生效!
然而我們用這種方式:s = append(s, "456"),這是在修改指針,並不是在修改指針指向的數據!
append可能會因爲s的cap不足,重新分配空間,所以append有一個返回參數。
我們對modifySlice中的s進行了修改,但是s是指針,僅僅是外層傳入的slice指針的拷貝,說明我們並沒有對外層的slice重新賦值!
這就是添加新元素失敗的原因。
如何改呢?
我們要對指針進行修改,指針是值傳遞,若想修改,自然要傳遞指針的指針啦!
於是我們可以修改成這樣:
package main
import (
"fmt"
"testing"
)
func TestModifySlice(t *testing.T) {
s := []string{"123"}
modifySlice(&s)
fmt.Println(s)
}
func modifySlice(s *[]string) {
(*s)[0] = "modify:123"
*s = append(*s, "456")
}
再來看運行結果:
完美解決了我們在上面遇到的問題~~
等等,是不是感覺寫法很難看?golang的特點是簡單易懂,這種寫法是c、c++式寫法,各種取地址、解引用,看起來很高端,但是理解起來要稍微轉個彎。
golang一般不會對傳入的參數進行修改,如果修改,以返回值的形式返回修改的內容。golang式寫法是簡單易懂的代表,寫法也很優美,直接將slice作爲返回值返回:
package main
import (
"fmt"
"testing"
)
func TestModifySlice(t *testing.T) {
s := []string{"123"}
s = modifySlice(s)
fmt.Println(s)
}
func modifySlice(s []string) []string {
s[0] = "modify:123"
s = append(s, "456")
return s
}
結果毫無疑問也是正確的。
現在想下,如果一開始就用golang式寫法,是不是就不會掉進這個坑裏了?答案是肯定的,這就說明代碼的風格與規範也是我們應該提高的一項技能呀……