Go 1.17中值得關注的幾個變化

pswyjz發表於2021-09-09

圖片描述

Go核心開發團隊在[去年GopherCon大會上給Go泛型的定調是在2022年2月份的Go 1.18版本中釋出],那可是自[Go誕生]以來語法規範變動最大的一次,這讓包括筆者在內的全世界的Gopher們都滿懷期待。

不過別忘了,在Go 1.18這個“網紅版本”釋出前,還有一個“實力派”版本Go 1.17呢!美國當地時間2021年8月16日,[Go 1.17版本在經過兩個RC版本之後正式釋出]!並且值得慶幸的是Go 1.17版本並沒有過多受到Go 1.18版本這個“網紅”的影響,Go 1.17默默地加入和最佳化了著實不少的特性。在這一篇文章中,我們就來看看Go 1.17版本中有哪些值得關注的變化。

1. 語言特性變化

,從誕生到現在語言自身特性變化很小,不會像其他主流語言那樣走“你有的我也要有”的特性融合路線。因此新語言特性對於Gopher來說屬於“稀缺品”,屬於“供不應求”那類事物_。這也直接導致了每次Go新版本釋出,我們都要首先看看語言特性是否有變更,每個新加入語言的特性都值得我們去投入更多關注,去深入研究。Go 1.17在語言特性層面做了兩方面的小改動,下面我們來看看。

第一個是對語言型別轉換規則的擴充套件,允許從切片到陣列指標的轉換,下面的程式碼在Go 1.17版本中是可以正常編譯和執行的:

// github.com/bigwhite/experiments/tree/master/go1.17-examples/lang/slice2arrayptr/main.go
func slice2arrayptr() {
    var b = []int{11, 12, 13}
    var p = (*[3]int)(b)
    p[1] = p[1] + 10
    fmt.Printf("%vn", b) // [11 22 13]
}

Go透過執行時對這類切片到陣列指標的轉換程式碼做檢查,如果發現越界行為,就會透過執行時panic予以處理。Go執行時實施檢查的一條原則就是“轉換後的陣列長度不能大於原切片的長度”,注意這裡是切片的長度(len),而不是切片的容量(cap)。

第二個變動則是unsafe包增加了兩個函式:Add與Slice。使用這兩個函式可以讓開發人員更容易地寫出符合[unsafe包使用的安全規則]的程式碼。這兩個函式原型如下:

// $GOROOT/src/unsafe.go
func Add(ptr Pointer, len IntegerType) Pointe
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType

unsafe.Add允許更安全的指標運算,而unsafe.Slice允許更安全地將底層儲存的指標轉換為切片。

2. go module的變化

自[Go 1.11版本引入go module]以來,每個Go大版本釋出時,go module都會有不少的積極變化,這是Go核心團隊與社群就go module深入互動的結果。

Go 1.17中go module同樣有幾處顯著變化,其中最最重要的一個變化就是pruned module graph(修剪的module依賴圖)。Go 1.17之前的版本某個module的依賴圖由該module的直接依賴以及所有間接依賴組成,無論某個間接依賴是否真正為原module的構建做出貢獻,這樣go命令在解決依賴時會讀取每個依賴的go.mod,包括那些沒有被真正使用到的module,這樣形成的module依賴圖被稱為完整module依賴圖(complete module graph)

Go 1.17不再使用“完整module依賴圖”,而是引入了pruned module graph(修剪的module依賴圖)。修剪的module依賴圖就是在完整module依賴圖的基礎上將那些“佔著茅坑不拉屎”、對構建完全沒有“貢獻”的間接依賴module修剪後的依賴圖。使用修剪後的module依賴圖進行構建將有助於避免下載或閱讀那些不必要的go.mod檔案,這樣Go命令可以不去獲取那些不相關的依賴關係,從而在日常開發中節省時間。

但module依賴圖修剪也帶來了一個副作用,那就是go.mod檔案size的變大。因為Go 1.17版本後,每次go mod tidy(當go.mod中的go版本為1.17時),go命令都會對main module的依賴做一次深度掃描(deepening scan),並將main module的所有直接和間接依賴都記錄在go.mod中(之前說的版本只記錄直接依賴)。考慮到內容較多,go 1.17將直接依賴和間接依賴分別放在兩個不同的require塊兒中。

3. 編譯器與執行時的變化

Go 1.17增加了對Windows上64位ARM架構的支援,讓開發者可以在更多裝置上原生執行Go。但這個版本編譯器最大的變化是在amd64架構下率先實現了從基於堆疊的呼叫慣例到[基於暫存器的呼叫慣例]。

並且,切換到[基於暫存器的呼叫慣例]後,一組有代表性的Go包和程式的基準測試顯示,Go程式的執行效能提高了約5%,二進位制檔案大小典型減少約2%。也就是說你的Go原始碼使用Go 1.17版本重新編譯一下就能獲得大約5%的效能提升,真希望這樣的最佳化越多越好!對更多平臺的基於暫存器呼叫慣例的支援將在未來的版本中出現。

除了改為基於暫存器的呼叫慣例之外,Go 1.17編譯器還支援包含[閉包]的函式的內聯(inline)了!這樣一來,一個帶有閉包的函式可能會在函式被內聯的每個地方產生一個不同的閉包程式碼指標,因此,
Go函式的值不能直接比較

Go編譯器還在Go 1.17中引入了//go:build形式的構建約束指示符,以替代原先易錯的// +build形式。

4. 其他變化

  • 保留龍芯架構GOARCH值

在Go 1.17版本中,Go編譯器保留了中國龍芯cpu架構的GOARCH值 - loong64。關於龍心GOARCH值選用loong64還是loongarch64還有過[一段激烈的爭論],最終大多數都贊同的loong64取得了最後的勝利。

  • Go test變化

Go test引入-shuffle的洗牌標誌位,用以控制單元測試或benchmark的執行順序。

另外T和B兩個型別分別都增加了Setenv方法用於在test和benchmark執行期間設定環境變數。

  • time包增加Time物件的GoString形式輸出

我們使用%#v輸出一個Time物件例項時,Go 1.17之前的版本輸出內容如下面:

Go 1.16.5輸出:

time.Time{wall:0xc03f08c0d06c9ed0, ext:83078, loc:(*time.Location)(0x11620e0)}

Go 1.17增加了GoString方法,該方法在Time物件以%#v格式輸出時被自動呼叫,其輸出結果如下:

time.Date(2021, time.August, 17, 20, 29, 42, 58245000, time.Local)

5. 小結

除上述變化之外,Go的其他標準庫隨著新版本的釋出也都會有大量的小改動,但每個開發人員對標準庫的關注點差別很大,因此,在這個系列中不會詳細做說明了,大家還是參考[Go 1.17的釋出說明文件]各取所需吧_

與傳統的“Go新版本值得關注的幾個變化”系列有所不同,本期內容較為簡單和概括,因為更多內容,我將在後續的Go 1.17新特性詳解系列中針對上述值得關注的新特性做進一步說明。


Go技術專欄“”正在慕課網火熱熱銷中!本專欄主要滿足廣大gopher關於Go語言進階的需求,圍繞如何寫出地道且高質量Go程式碼給出50條有效實踐建議,上線後收到一致好評!歡迎大家訂
閱!

圖片描述

我的網課“”在慕課網熱賣中,歡迎小夥伴們訂閱學習!

圖片描述


講師主頁:
講師部落格:
專欄:
實戰課:
免費課:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/755/viewspace-2797295/,如需轉載,請註明出處,否則將追究法律責任。

相關文章