前言
Go官方團隊在2022.12.08釋出了Go 1.20 rc1(release candidate)版本,Go 1.20的正式release版本預計會在2023年2月份釋出。
讓我們先睹為快,看看Go 1.20給我們帶來了哪些變化。(文末有彩蛋!)
安裝方法:
$ go install golang.org/dl/go1.20rc1@latest
$ go1.20rc1 download
這是Go 1.20版本更新內容詳解的第1篇,歡迎大家關注公眾號,及時獲取本系列最新更新。
Go 1.20釋出清單
和Go 1.19相比,改動內容適中,主要涉及語言(Language)、可移植性(Ports)、工具鏈(Go Tools)、執行時(Runtime)、編譯器(Compiler)、彙編器(Assembler)、連結器(Linker)和核心庫(Core library)等方面的最佳化。
我們逐個看看具體都有哪些變化。
語言變化
Go 1.20在語言層面帶來了4個變化。
slice轉陣列
Go1.17在語言層面開始支援將slice轉為指向陣列的指標。
示例如下:
s := make([]byte, 2, 4)
// 將s這個slice轉為指向byte陣列的指標s0
// 其中[0]byte裡的0表示陣列的長度,雖然長度為0,但值不等於nil
s0 := (*[0]byte)(s) // s0 != nil
fmt.Printf("%T")
// 將s[1:]這個slice轉為指向byte陣列的指標s1
// s1指向的陣列的長度為1
s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1]
// 將s這個slice轉為指向byte陣列的指標s2
// s2指向的陣列的長度為2
s2 := (*[2]byte)(s) // &s2[0] == &s[0]
// 將s這個slice轉為指向byte陣列的指標s4
// s4指向的陣列的長度為4
s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s)
注意:slice轉為指向陣列的指標時,如果陣列定義的長度超過了slice的長度,會拋panic。
所以上面s4 := (*[4]byte)(s)
這行程式碼雖然可以編譯透過,但是會出現runtime panic。
Go 1.20之前不支援將slice直接轉為陣列,如果要轉,得先轉為指向陣列的指標,再轉為陣列,如下面程式碼所示:
s := make([]byte, 2, 4)
s[0] = 100
s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1]
s2 := (*[2]byte)(s) // &s2[0] == &s[0]
fmt.Printf("%T, %v, %p, %p\n", s1, s1[0], &s1[0], &s[1])
fmt.Printf("%T, %v, %v, %p\n", s2, s2[0], &s2[0], s)
// a1陣列裡元素的地址和s1指向的陣列的元素地址不一樣,a2同理
a1 := *s1
a2 := *s2
fmt.Printf("%T, %v, %p, %p\n", a1, a1[0], &a1[0], &s1[0])
fmt.Printf("%T, %v, %p, %p\n", a2, a2[0], &a2[1], &s2[1])
從Go 1.20開始,支援將slice直接轉為陣列,如下面程式碼所示:
s := make([]byte, 2, 4)
s[0] = 100
s1 := [1]byte(s[1:])
s2 := [2]byte(s)
// s1陣列裡元素的地址和s指向的陣列的元素地址不一樣,s2同理
fmt.Printf("%T, %v, %p, %p\n", s1, s1[0], &s1[0], &s[1])
fmt.Printf("%T, %v, %v, %p\n", s2, s2[0], &s2[0], s)
總結:
- slice轉為指向陣列的指標後,這個指標會指向和slice相同的地址空間
- slice轉為陣列時,會把slice底層陣列的值複製一份出來。轉換後得到的陣列的地址空間和slice底層陣列空間不一樣。
還有幾個語法細節可以參考如下程式碼示例:
var t []string
t0 := [0]string(t) // ok for nil slice t
t1 := (*[0]string)(t) // t1 == nil
t2 := (*[1]string)(t) // panics: len([1]string) > len(t)
u := make([]byte, 0)
u0 := (*[0]byte)(u) // u0 != nil
Comparable型別
Go泛型裡comparable這個型別約束(type constraint)有個坑,就是和Go語言裡定義的可比較型別(Comparable types)並不一致。
什麼是comparable types,簡單來說就是可以用==
和!=
來進行比較的型別就是comparable types。
The equality operators
==
and!=
apply to operands that are comparable.The ordering operators
<
,<=
,>
, and>=
apply to operands that are ordered.
有些可比較型別的變數不能作為型別實參(type argument)賦值給宣告瞭comparable型別約束的型別引數(type parameter)。
例如Go語言說明裡有如下這段內容:
Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil
.
這裡明確指出,介面型別的值是可比較的,但是我們不能把2個interface作為型別實參給到型別引數。
參考如下程式碼示例:
// example4.g0
package main
import "fmt"
func IsEqual[T comparable](a T, b T "T comparable") bool {
return a == b
}
func main() {
var a interface{} = 1
var b interface{} = []int{1}
fmt.Println(a == b) // false
// go1.20之前的版本編譯報錯,go1.20開始支援
fmt.Println(IsEqual(a, b))
}
對於上面最後一行程式碼,Go 1.20之前的版本編譯報錯。
$ go1.18 run example4.go
./example4.go:13:21: interface{} does not implement comparable
因為Go 1.20之前的版本認為空介面型別interface{}並沒有實現comparable型別約束,不能作為型別實參傳給型別引數。
從Go 1.20版本開始,不會編譯報錯,因為interface型別是comparable type,程式執行結果如下:
$ go1.20rc1 run example4.go
false
false
具體哪些型別是comparable type可以參考:Comparable types 裡的說明。
unsafe包
Go 1.17版本在unsafe package裡引入了Slice函式,如下所示:
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
在Go 1.20版本里,標準庫unsafe package定義了3個新的函式:
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte
有了這4個函式,可以構造和解構slice和string。
具體細節可以參考:https://tip.golang.org/ref/sp...。
值比較
Go語言說明現在明確指出結構體變數的值每次只比較一個欄位,欄位比較的順序和欄位在結構體裡定義的順序保持一致。
一旦某個欄位的值比較出現不一致,就會馬上停止比較。
以前的說明可能會讓Go開發者有誤解,以為結構體變數的比較需要比較所有欄位,實際並不是。
類似的,陣列的比較也是每次只比較一個元素,按照陣列的下標索引由小到大逐個比較陣列裡每個元素的值。
這塊只是改了說明而已,對大家的程式碼沒有任何影響。
可移植性
Darwin and iOS
Go 1.20將會成為支援macOS 10.13 High Sierra和10.14 Mojave的最後一個版本。
如果未來想在mac電腦上使用Go 1.21或者更新的Go版本,只能用macOS 10.15 Catalina和更新的macOS版本。
FreeBSD/RISC-V
Go 1.20增加了對於RISC-V架構在FreeBSD作業系統的實驗性支援。
GOOS=freebsd
GOARCH=riscv64
總結
下一篇會介紹Go 1.20在Go Tool工具鏈、執行時、編譯器、彙編器、連結器和核心庫的最佳化工作,有一些內容值得學習,歡迎大家保持關注。
開源地址
文章和示例程式碼開源在GitHub: Go語言初級、中級和高階教程。
公眾號:coding進階。關注公眾號可以獲取最新Go面試題和技術棧。
個人網站:Jincheng's Blog。
知乎:無忌。
福利
我為大家整理了一份後端開發學習資料禮包,包含程式語言入門到進階知識(Go、C++、Python)、後端開發技術棧、面試題等。
關注公眾號「coding進階」,傳送訊息 backend 領取資料禮包,這份資料會不定期更新,加入我覺得有價值的資料。還可以傳送訊息「進群」,和同行一起交流學習,答疑解惑。
最後送上一個彩蛋,Go標準庫的腦圖,想學習Go標準庫的可以參考這個來。