大家好,我是煎魚。
最近 Go1.20 在 2 月初已經正式發布,來的挺早的,以往都是拖到月底的,我翻了好幾篇,發現最后還是做了不少功能取舍,被迫放了某些新特性(例如:arena 等)的鴿子!
懷疑 Go 團隊 2 月份是有什么事要干,或者打算休假?又或是擔心裁員影響工作交接?
今天快速溫習與我們關聯性多些的新特性,看看升不升 1.20。
之前 Go1.18 正式發布了泛型,有歡喜也有憂。這雖然支持了泛型,但是在 Go1.18 起編譯速度相對 Go1.17 變慢了,會慢大概 15-18%,降速明顯。

泛型特性,把 Go 引以為豪的構建速度都變慢了。怕不是以后構建也可以沖咖啡喝了?
原本說在 Go1.19 修,鴿了。終于,當前版本已經修復了。
如下測試報告:
????????????????│?117.results?│????????????118.results????????????│????????????119.results????????????│????????????tip.results????????????│
????????????????│???sec/op????│???sec/op????vs?base???????????????│???sec/op????vs?base???????????????│???sec/op????vs?base???????????????│
GoBuildKubelet?????52.58?±?0%???56.54?±?1%??+7.54%?(p=0.000?n=10)???55.47?±?1%??+5.50%?(p=0.000?n=10)???51.41?±?1%??-2.22%?(p=0.000?n=10)
GoBuildIstioctl????47.78?±?1%???51.44?±?0%??+7.65%?(p=0.000?n=10)???50.89?±?5%??+6.50%?(p=0.000?n=10)???46.05?±?1%??-3.62%?(p=0.000?n=10)
GoBuildFrontend????19.03?±?1%???20.55?±?1%??+7.99%?(p=0.000?n=10)???20.04?±?0%??+5.33%?(p=0.000?n=10)???18.22?±?1%??-4.27%?(p=0.000?n=10)
geomean????????????36.29????????39.10???????+7.72%??????????????????38.39???????+5.77%??????????????????35.07???????-3.37%
最新的 Go1.20 基準測試中,當前版本和 Go1.17 的構建速度保持一致。
另外還優化了編譯器和垃圾收集器,減少了內存開銷,將整體 CPU 性能提高了 2%。
Go1.20 的更新公告中也宣布了一個重大的更新結束通知,涉及 macOS 和 windows 操作系統。

分別如下:
Go1.20 是支持 macOS 10.13 High Sierra 或 10.14 Mojave 上運行的最后一個版本。Go 1.21 將需要 macOS 10.15 Catalina 或更高版本。
Go1.20 是支持 Windows 7、8、Server 2008 和 Server 2012 的任何版本上運行的最后一個版本。Go 1.21 將至少需要 Windows 10 或 Server 2016。
好家伙,看來我需要更新我的操作系統版本了,不然下個版本 Go 都不歡迎我 coding 了。

有需要的同學在下個版本前盡早做好升級。
新版本起,Go 的 $GOROOT/pkg 目錄將不再存儲標準庫的預編譯包存檔,Go 發行版的將迎來一輪瘦身。
大小對比如下。
Go1.20:

Go1.19:

約比老版本縮減了 1/3,還是比較明顯的。
在 Go1.20 起,Go 引入了 Profile-guided optimization (PGO),翻譯過來是使用配置文件引導的優化,當前為預覽版本。
PGO 是一門編譯器優化技術,能夠在不改業務代碼的情況下,給你的應用程序帶來一定的性能提升。在 Go PGO 中將會依托 runtime/pprof 所生成的 profile 來完成。

結果上可以使得 Go tool(工具鏈)能根據運行時信息執行特定于應用程序和工作負載的優化。說明了就是想提高性能,不需要改業務代碼。
具體可以詳見:《PGO 是啥,咋就讓 Go 更快更猛了?》
在原有 Go1.13 的 errors API 上進行新增和修改,核心是支持一個錯誤可以封裝多個錯誤的特性。
新特性例子:
func?main()?{
?err1?:=?errors.New("err1")
?err2?:=?errors.New("err2")
?err?:=?errors.Join(err1,?err2)
?fmt.Println(err)
?if?errors.Is(err,?err1)?{
??fmt.Println("err?is?err1")
?}
?if?errors.Is(err,?err2)?{
??fmt.Println("err?is?err2")
?}
}
輸出結果:
err1
err2
err?is?err1
err?is?err2
具體可以詳見:《Go1.20 繼續小修小補 errors 庫。。。》
Go 團隊通過分析、搜索發現 reflect.SliceHeader 和 reflect.StringHeader:
type?StringHeader?struct?{
???Data?uintptr
???Len??int
}
在業內經常被濫用,使用不方便,很容易出現隱性問題。例如:Data 字段類型是 uintptr 不是 unsafe.Pointer。設什么都可以,靈活度過于高,非常容易搞出問題。
在 Go1.20 起,在 unsafe 標準庫新增了 3 個函數來替代前面這兩個類型的使用。希望能夠進一步標準化,并提供額外的類型安全。

如下函數簽名:
func String(ptr *byte, len IntegerType) string:根據數據指針和字符長度構造一個新的 string。func StringData(str string) *byte:返回指向該 string 的字節數組的數據指針。func SliceData(slice []ArbitraryType) *ArbitraryType:返回該 slice 的數據指針。新版本的用法變成:
func?StringToBytes(s?string)?[]byte?{
????return?unsafe.Slice(unsafe.StringData(s),?len(s))
}
func?BytesToString(b?[]byte)?string?{
????return?unsafe.String(&b[0],?len(b))
}
以往常用的 reflect.SliceHeader 和 reflect.StringHeader 將會被標注為被廢棄。
具體可以詳見:《別亂用了,用新的。Go SliceHeader 和 StringHeader 將會被廢棄!》
有很多 Go 同學反饋老要記 2006-01-02 15:04:05,發現這個日期時間點,使用的次數非常高頻:
| 排名 | 頻率 | 格式 |
|---|---|---|
| 1 | 75616 | time.RFC3339 |
| 2 | 23954 | time.RFC3339Nano |
| 3 | 13312 | "2006-01-02 15:04:05" |
| 4 | 12332 | "2006-01-02" |
| 5 | 11940 | time.RFC1123 |
使用頻率的數據有理有據。
Go1.20 加了以下常量,便于直接引用:
DateTime???=?"2006-01-02?15:04:05"
DateOnly???=?"2006-01-02"
TimeOnly???=?"15:04:05"
再者就是新增了時間比較的方法。
在現在的標準庫中,有 3 個方法來比較 time.Time 對象,分別是:Before()、Equal() 和 After(),作用上類似 <、== 和 >。但缺少 <= 和 >= 的等價物。
Go1.20 將會支持 Time.Compare,以此來達到類似的效果。作用是將 Time 對象 t 和 u 兩者進行比較。
func?(t?Time)?Compare(u?Time)?int
該方法返回如下幾種結果:
具體可以詳見:《Go1.20 中兩個關于 Time 的更新,終于不用背 2006-01-02 15:04:05 了!》
以前可以做匿名接口循環導入的騷操作。如下代碼:
type?I?interface?{
?m()
?interface?{
?????I
??}
}
這段代碼,聲明了接口類型 I,然后又包含了 m(),又包含接口 I。這會是一個 “永動機”,永遠都不會停止。在開源的 GitHub 中,也真實存在著。
Go1.20 起,編譯器將會默認拒絕匿名接口循環導入。如果沒有用戶反饋受到了重大的影響或問題,將會計劃在 Go1.22 中正式的禁用和移除該項功能的支持。
具體可以詳見:《Go1.20 將禁止匿名接口循環導入!》
Go1.20 將會在沒有 C 工具鏈的系統上默認禁用 CGO。這理論上是一個不兼容性設置,如果大家有需要,可以提前設置好 CGO_ENABLED 環境變量,以避免導致部分應用程序出問題。
Go1.20 起支持將切片轉換成數組。
如下代碼:
func?main()?{
?v?:=?[]string{"煎",?"魚",?"進",?"腦",?"子",?"了"}
?s?:=?[6]string(v)
?fmt.Println(s)
}
當然,前提是切片和數字的長度和類型都要對的上。否則會出現如下報錯:
panic:?runtime?error:?cannot?convert?slice?with?length?5?to?array?or?pointer?to?array?with?length?6
goroutine?1?[running]:
main.main()
?/tmp/sandbox1162344488/prog.go:9?+0x1d
Program?exited.
在本次 Go1.20 的更新中,比較有意思的是 PGO 的預覽版本,大家有機會可以體驗下不改代碼就提高應用性能的快感。而相關的更新有的是在償還技術債務。例如:編譯加速等。
原本關注度很高的 arean,之前在《打臉了兄弟們,Go1.20 arena 來了!》中我分享過。在 Go 團隊在具體實現和分析后,發現現有的 API 存在嚴重的問題,暫時回滾迭代代碼了,因此放了鴿子。這塊我計劃后面再單獨分享。
尤其需要注意的,Go1.21 起將不再支持 macOS、windows 部分版本。可能一些公司的機器,甚至自己的要提前升級了。
如果希望查看完整發布記錄,可以閱讀:https://go.dev/doc/go1.20。
關注和加煎魚微信,
一手消息和知識,拉你進技術交流群??


你好,我是煎魚,出版過 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領域最有觀點專家)榮譽,點擊藍字查看我的出書之路。
日常分享高質量文章,輸出 Go 面試、工作經驗、架構設計,加微信拉讀者交流群,和大家交流!