今天是週末的,雖然只有一天。給大家分享一個 Go1.17 的小快訊。一天增長一個吸魚小技巧!
在Go 語言中,一個切片(slice)包含了對其支援陣列的引用,無論這個陣列是作為一個獨立的變數存在於某個地方,還是只是一個為支援分片而分配的匿名數組。
其切片基本結構都如下:
// runtime/slice.go type slice struct { array unsafe.Pointer // 指向底层数组的指针 len int // 长度 cap int // 容量 }
目前切片這種支援陣列的方式可能會導致切片出現有趣的記憶體洩漏或對你的切片產生令人驚訝的變化。
另外很重要的一點,在 Go 1.16 及以前,不存在將從切片類型轉換為陣列類型的安全方法,比較無奈。
我們只能透過呼叫標準函式庫reflect 或unsafe,透過寫不安全的程式碼來做到這一點:
(*[10]byte)(unsafe.Pointer(&b[0]))
顯然這是不優雅的,官方自己都不推薦使用unsafe ,一旦處理出錯了,可能還會導致致命錯誤,比較不可控。
其實早在2009 年,在Go 發布後不久(遠在Go 1.0 發布之前),就有人提出相關疑惑,希望解決這個問題:
終於,在即將到來的Go 1.17 中,這將成為可能,因為從commit-id #1c268431f4 開始的一系列變化,更新了規範:
#新的規範中對此的描述很直接:
Converting a slice to an array pointer yields a pointer to the underlying array of the slice. If the length of the slice is less than the length of the array, a run-time panic occurs.
- 如果切片的長度比數組的長度長是無害,能夠正常運作。
- 如果比陣列長的切片,表示你的陣列將無法存取原始切片的所有支援數組。
另外規格中提供了一些新的例子,在Go1.17 中我們可以這麼用:
s := make([]byte, 2, 4) s0 := (*[0]byte)(s) // s0 != nil s2 := (*[2]byte)(s) // &s2[0] == &s[0] s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s) var t []string t0 := (*[0]string)(t) // t0 == nil t1 := (*[1]string)(t) // panics: len([1]string) > len(s)
- 變數s2 的轉換:其將切片底層的陣列轉換了出來,這種轉換不會(也不能)分配一個新的數組,從而保證了它的效率。
- 變數 s0 和 t0 的轉換:其將一個非空的片段轉換為一個 0 長度的陣列。雖然長度為 0 的數組,你不能用它做任何事情,但依然必須給一個有效的指針,也就是 nil。
需要注意,現在還沒有辦法像型別斷言那樣,檢查他是否會因為越界等原因出現 panic 事件。如果你認為你可能有一個太短的片段,可能會導致 panic 事件,那麼你需要使用 if 來進行預判斷。
同時標準庫 reflect 也會進行更新,以便於支援從切片到陣列指標的轉換,如果你正在用 reflect 做相關轉換工作,建議閱讀該提交中的注意事項。
你對 Go 語言在型別轉換上有沒有其它的一些想法和訴求#,或是有沒有踩過什麼坑?
歡迎大家在留言區留言交流。