Go 1-18 新特性:範型
Go 語言的 1.18 版本還未正式發佈,但我們已經可以用它的 RC 版本嘗試一些新特性。今天來聊聊 Go 1.18 裏的範型(Generics)。
安裝
Generics 是 1.18 的新特性,在目前的發佈版 1.17 裏不支持,因此需要手動安裝 RC 版:
go install golang.org/dl/go1.18rc1@latest
安裝完後,你的GOPATH
或者GOBIN
裏會出現一個go1.18rc1
的可執行文件。然後下載 SDK:
go1.18rc1 download
此後編譯和運行 go 代碼,需要使用這個可執行文件,或者你也可以用 alias 把 go 指向這個 RC 版本,這樣就不用每次打版本號了:
alias go=go1.18rc1
使用 Generics
看一個簡單的例子,假如我們要實現一個函數,輸入是一個名字(string)到整數數值(int64)的映射表,返回是這個映射表裏所有數值的總和:
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
很簡單的實現。但是,如果我們想要也能處理浮點數值(float64)呢?是否要寫個類似的函數:
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
這樣做也不是不行,但是有很多重複代碼,使用起來也不方便。
看看用 Generics 的實現:
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
解釋一下:
-
方括號裏的部分,聲明瞭類型參數:
[K comparable, V int64 | float64]
,這裏聲明瞭兩個類型,K 是內置的 comparable 類型,任何類型只要能進行比較(==, !=)就屬於 comparable;V 可以是 int64 或 float64 之一,也就是這兩個類型的 union,V 這樣的又稱爲 “類型限制”(type constraint),因爲它限制了 V 可能的類型。 -
然後在函數的參數部分,用這兩個類型 K 和 V 來聲明參數:
m map[K]V
,也就是說輸入參數是一個類型 K 到類型 V 的映射表。因爲 map 要求 key 必須是可比較的,這裏 K 類型聲明爲 comparable 就保證了這一點。 -
函數的返回值,也是類型 V。
這樣,我們只需要一個實現就能支持多種類型了。
如何調用這個範型函數?也很簡單:
ints := map[string]int64{
"first": 12,
"second": 34,
}
floats := map[string]float64{
"first": 12.34,
"second": 22.55,
}
fmt.Printf("SumInts: %v\n",
SumIntsOrFloats[string, int64](ints))
fmt.Printf("SumFloats: %v\n",
SumIntsOrFloats[string, float64](floats))
在這個例子裏,我們調用範型函數的時候加上了類型參數,例如[string, int64]
。實際上,這個類型參數在很多時候是可以省略的,Go 會根據函數的參數自動推導類型:
fmt.Printf("SumInts: %v\n",
SumIntsOrFloats(ints))
fmt.Printf("SumFloats: %v\n",
SumIntsOrFloats(floats))
當然這種自動類型推導並不是在所有情況下都可用,比如如果函數沒有參數,那就沒法推導了,調用時必須加上類型參數。
另一個比較好的習慣是,把範型函數的類型參數提取到 interface 裏,可以提高代碼的可讀性和可維護性:
type Number interface {
int64 | float64
}
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
這裏把 V 的類型提取到 Numbers 這個 interface 裏,Numbers 裏定義了類型限制(int64 或 float64)。這樣範型函數 SumNumbers 就更簡潔,並且如果還有其他的範型函數用到它的話,我們不用到處複製 int64 | float64
,可維護性大大提高。
Go 1.18 的 Generics 特性就先聊到這裏。感謝閱讀!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/CdxGSxz3k389dfT2hLgJtg