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
}

解釋一下:

這樣,我們只需要一個實現就能支持多種類型了。

如何調用這個範型函數?也很簡單:

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