Go 1.20要來了,看看都有哪些變化-第1篇

coding進階發表於2022-12-15

前言

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標準庫的可以參考這個來。

References

相關文章