Slice 是看向底層 array 的一扇窗戶
Slice 本身不存資料。它是一個三欄位的小結構(slice header),資料躺在它指向的那塊 array 裡。
slice header = { ptr → 底層 array 的某個位置, len, cap }- ptr — 指向底層 array 的起點(是這個 slice 的起點,不一定是 array 的起點)
- len — 這扇窗戶目前看得到幾個元素
- cap — 從自己的起點算到底層 array 的最後,最多還能往右拉多寬
len 與 cap 管的是兩件不同的事:一個是「現在看得到多少」,一個是「最多允許碰到哪裡」。後者才是所有詭異行為的根源。
s := []int{2, 3, 5, 7, 11, 13} // len=6 cap=6
s = s[:0] // len=0 cap=6 []
s = s[:4] // len=4 cap=6 [2 3 5 7]
s = s[2:] // len=2 cap=4 [5 7] ← 起點右移,cap 跟著變小起點一往右移,左邊的元素就再也拿不回來——slice 只能向右擴展,不能向左。
為什麼需要 slice 而不是直接用 array
Go 的 array 長度是型別的一部分([4]int 與 [5]int 是不同型別),開好就不能改;而且 array 是值型別,賦值與傳參都會整份複製。
slice 把「多長」從型別裡拿掉,複製時也只複製那個三欄位的 header(成本與資料量無關),所以日常講的「陣列」實際上都是 slice。
相關:Go 的切割上限是 cap 而不是 len、Go 傳 slice 是複製 header 而不是 pass by reference、Go 的 range 回傳 index 與 value