iT邦幫忙

0

Golang - Generics 泛型

  • 分享至 

  • xImage
  •  

前言

去面試發現蠻多公司會問關於 Go 的 GC 跟 GMP,和其他底層的原理
花了點時間搞懂之後技術欠債清單上的大石頭又少了幾個,不過還是要繼續還債XD

Go 推出泛型已經有一段時間了
雖然在實際作業時程式碼不會看不懂,不過要寫的時候還是會卡卡的
找個時間練習一下,畢竟以前也沒用過

這篇的原文是 Go 官方的泛型教學,可以在參考連結找到
更深入的實作原理跟效能測試請參考連結

什麼是泛型

一般在撰寫程式時,如果有同樣的 function,只是實作方法不同
此時我們可以將 function 抽象化為 interface,由實際的對象來實作

泛型也是一樣的概念,只是改為將 parameter 抽象化
這樣就不需要撰寫相同實作,只是類型不同的程式碼
可以由 Go 官網教學提供的程式碼來看看實際的例子
可以發現這段程式碼就是需要泛型的原因
做的實作是差不多的,只有因為參數類型不同,就要多寫一次

func main() {
	// Initialize a map for the integer values
	ints := map[string]int64{
		"first":  34,
		"second": 12,
	}

	// Initialize a map for the float values
	floats := map[string]float64{
		"first":  35.98,
		"second": 26.99,
	}

	fmt.Printf("Non-Generic Sums: %v and %v\n",
		SumInts(ints),
		SumFloats(floats))
}

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
	var s int64
	for _, v := range m {
		s += v
	}
	return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
	var s float64
	for _, v := range m {
		s += v
	}
	return s
}

使用泛型

泛型是在 Go 1.18 以後才有的功能
泛型是在 Go 1.18 以後才有的功能
泛型是在 Go 1.18 以後才有的功能
所以版本不到是用不了的

接著來加入兩段程式碼來觀察變化,來改良之前要重複寫好幾次的問題

fmt.Printf("Generic Sums: %v and %v\n",
		SumIntsOrFloats[string, int64](ints),
		SumIntsOrFloats[string, float64](floats))
// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
	var s V
	for _, v := range m {
		s += v
	}
	return s
}
  1. 如果把這段程式碼放到 IDE 內會發現 SumIntsOrFloats[string, int64](ints) SumIntsOrFloats[string, float64](floats) 這兩行程式碼的型別宣告是多餘的,可以刪除,但是在沒有參數的情況下還是需要宣告型別
  2. 在 [K comparable, V int64 | float64] 這段的意思是指約束函式的參數型別
  3. comparable 如果你有用 IDE 可以去找 reference 會發現說明
// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, arrays of comparable types,
// structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable interface{ comparable }
  1. any 是新的保留字

簡化泛型程式碼

對於泛型,可以使用 interface 來宣告

type Number interface {
    int64 | float64
}

在原來的函式就可以改為用 interface 作為約束

// SumNumbers sums the values of map m. It supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

完成後的程式碼可以在官方教學裡面找到

何時適合使用泛型

https://go.dev/blog/when-generics

泛型原理以及性能

https://zhuanlan.zhihu.com/p/509290914
https://www.dolthub.com/blog/2022-04-01-fast-generics/
https://planetscale.com/blog/generics-can-make-your-go-code-slower

參考資料

  1. https://go.dev/doc/tutorial/generics
  2. https://go.dev/blog/when-generics
  3. https://zhuanlan.zhihu.com/p/509290914
  4. https://www.dolthub.com/blog/2022-04-01-fast-generics/
  5. https://planetscale.com/blog/generics-can-make-your-go-code-slower

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言