go是宣揚(yáng)實(shí)用主義的語(yǔ)言枚冗,很多時(shí)候都把c中的最佳實(shí)踐直接規(guī)定成語(yǔ)法了。其中之一就是slice,簡(jiǎn)單但是非常容易踩坑壁晒。
先看一個(gè)小例子:
func main() {
a := make([]int, 2, 2)
a[0], a[1] = 1, 2
b := append(a[0:1], 3)
c := append(a[1:2], 4)
fmt.Println(b,c)
}
在這個(gè)小例子中,原本是希望將a[0:1]
作為b的前綴业栅,然后追加上3秒咐;將a[1:2]
作為c的前綴谬晕,然后追加上4。但實(shí)際上輸出結(jié)果并不是原本期望的[1 3] [2 4]
携取,而變成了[1 3] [3 4]
攒钳。這是為什么呢?
我們知道數(shù)據(jù)結(jié)構(gòu)中數(shù)組是非常高效的雷滋,可以直接尋址不撑,但是有個(gè)缺陷,難以擴(kuò)容晤斩。所以slice被設(shè)計(jì)為指向數(shù)組的指針焕檬,在需要擴(kuò)容時(shí),會(huì)將底層數(shù)組上的值復(fù)制到一個(gè)更大的數(shù)組上然后指向這個(gè)新數(shù)組澳泵。
slice有個(gè)特性是允許多個(gè)slice指向同一個(gè)底層數(shù)組实愚,這是一個(gè)有用的特性,在很多場(chǎng)景下都能通過(guò)這個(gè)特性實(shí)現(xiàn) no copy 而提高效率兔辅。但共享同時(shí)意味著不安全腊敲。b在追加3時(shí)實(shí)際上覆蓋了a[1]
,導(dǎo)致c變成了[3 4]
维苔。
怎么解決呢碰辅?防止共享數(shù)據(jù)的出現(xiàn)問(wèn)題需要注意兩條,只讀和復(fù)制蕉鸳,或者統(tǒng)一歸納為不可變乎赴。
寫法1,make出一個(gè)新slice潮尝,然后先copy前綴到新數(shù)組上再追加:
func main() {
a := make([]int, 2, 2)
a[0], a[1] = 1, 2
b := make([]int, 1)
copy(b, a[0:1])
b = append(b, 3)
c := make([]int, 1)
copy(c, a[1:2])
c = append(c, 4)
fmt.Println(b, c)
}
寫法2榕吼,利用go中slice的一個(gè)小眾語(yǔ)法,a[0:1:1] (源[起始index勉失,終止index羹蚣,cap終止index])
,強(qiáng)迫追加時(shí)復(fù)制到新數(shù)組乱凿。
func main() {
a := make([]int, 2, 2)
a[0], a[1] = 1, 2
b := append(a[0:1:1], 3)
c := append(a[1:2:2], 4)
fmt.Println(b, c)
}