Go 1.18 中引入的泛型徹底改變了編寫可重複使用和類型安全程式碼的方式。泛型帶來了靈活性和強大的功能,同時保持了 Go 的簡單哲學。然而,要了解細微差別、優點以及泛型與傳統方法(如 interface{} )的比較,需要仔細觀察。
讓我們探索泛型的複雜性,深入研究約束,將泛型與介面{}進行比較,並示範它們的實際應用。我們還將討論性能考慮因素和二進制大小的影響。讓我們開始吧!
泛型允許開發人員編寫可以在任何類型上操作的函數和資料結構,同時保持類型安全。泛型不依賴涉及執行時間類型斷言的介面{},而是讓您指定一組約束來規定類型上允許的操作。
文法
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
T: 型態參數,表示型別的佔位符。
TypeConstraint:將 T 的類型限制為特定類型或一組類型。
parameterName T:參數使用泛型類型T.
ReturnType:函數也可以傳回 T.
類型的值範例
func Sum[T int | float64](a, b T) T { return a + b }
func Sum: 宣告函數名稱 Sum
[T int | float64]: 指定引入T 作為類型參數的類型參數列表,約束為特定類型(int 或float64 )。 Sum 函數只能採用 int 或 float64 參數,不能組合使用,兩者都必須是 int 或 float64。我們將在下面的部分中進一步探討這一點。
(a, b T): 宣告兩個參數a 和b,皆為T 類型(泛型型別) ).
T: 指定函數的回傳類型,與型別參數 T.
相符約束定義哪些操作對於泛型類型有效。 Go 提供了強大的約束工具,包括實驗性約束套件(golang.org/x/exp/constraints)。
Go 引入了具有泛型的內建約束,以提供類型安全,同時允許靈活地定義可重複使用和泛型程式碼。這些約束使開發人員能夠對泛型函數或類型中使用的類型強制執行規則。
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
func Sum[T int | float64](a, b T) T { return a + b }
實驗限制
func PrintValues[T any](values []T) { for _, v := range values { fmt.Println(v) } }
自訂限制是定義泛型類型參數必須滿足的一組類型或類型行為的介面。透過創建您自己的約束,我們可以;
將類型限制為特定子集,例如數字類型。
需要型別來實作特定的方法或行為。
為您的通用函數和類型添加更多控制和特異性。
文法
func CheckDuplicates[T comparable](items []T) []T { seen := make(map[T]bool) duplicates := []T{} for _, item := range items { if seen[item] { duplicates = append(duplicates, item) } else { seen[item] = true } } return duplicates }
範例
import ( "golang.org/x/exp/constraints" "fmt" ) func SortSlice[T constraints.Ordered](items []T) []T { sorted := append([]T{}, items...) // Copy slice sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) return sorted } func main() { nums := []int{5, 2, 9, 1} fmt.Println(SortSlice(nums)) // Output: [1 2 5 9] words := []string{"banana", "apple", "cherry"} fmt.Println(SortSlice(words)) // Output: [apple banana cherry] }
Sum 函數 只能使用 int、int64 和 float64 參數呼叫。
如果你想強制型別必須實作某些方法,你可以使用這些方法來定義它。
type Numeric interface { int | float64 | uint }
Formatter 約束要求任何用作T 的類型必須具有傳回字串.
組合約束
type Number interface { int | int64 | float64 } func Sum[T Number](a, b T) T { return a + b }
int、float54),並且需要有 abs 方法。
泛型與介面{}類型安全
介面{}:由於額外的執行時間類型檢查,速度較慢。
泛型:更快,因為編譯器會產生特定於類型的最佳化程式碼路徑。
介面{}:通常冗長且不太直觀,使程式碼更難維護。
泛型:更簡潔的語法可以帶來更直覺和可維護的程式碼。
interface{}:產生更小的二進位文件,因為它不會為不同類型重複程式碼。
泛型:由於類型專門化而略微增加二進位大小以獲得更好的性能。
範例
func FunctionName[T TypeConstraint](parameterName T) ReturnType { // Function body using T }
程式碼運作良好,型別斷言是開銷。 Add 函數可以使用任何參數調用,a 和 b 參數可以是不同的類型,但是程式碼會在運行時崩潰。
func Sum[T int | float64](a, b T) T { return a + b }
泛型消除了由不正確的類型斷言引起的運行時恐慌的風險並提高了清晰度。
泛型為每種類型產生專門的程式碼,與介面{}相比,可以帶來更好的執行時間效能。
存在一個權衡:泛型由於每種類型的程式碼重複而增加了二進位大小,但這與好處相比通常可以忽略不計。
約束的複雜性: 雖然像 Constraints.Ordered 這樣的限制簡化了常見用例,但定義高度自訂的約束可能會變得冗長。
結構體中沒有類型推論: 與函數不同,您必須為結構體明確指定類型參數。
func PrintValues[T any](values []T) { for _, v := range values { fmt.Println(v) } }
僅限於編譯時約束:Go 泛型專注於編譯時安全,而 Rust 這樣的語言使用生命週期和特徵提供更強大的限制。
我們將實作一個具有介面{}和通用的簡單佇列,並對結果進行基準測試。
func CheckDuplicates[T comparable](items []T) []T { seen := make(map[T]bool) duplicates := []T{} for _, item := range items { if seen[item] { duplicates = append(duplicates, item) } else { seen[item] = true } } return duplicates }
import ( "golang.org/x/exp/constraints" "fmt" ) func SortSlice[T constraints.Ordered](items []T) []T { sorted := append([]T{}, items...) // Copy slice sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) return sorted } func main() { nums := []int{5, 2, 9, 1} fmt.Println(SortSlice(nums)) // Output: [1 2 5 9] words := []string{"banana", "apple", "cherry"} fmt.Println(SortSlice(words)) // Output: [apple banana cherry] }
type Numeric interface { int | float64 | uint }
執行時間:
通用實作比 interface{} 版本快約 63.64%,因為它避免了運行時類型斷言並直接對給定類型進行操作。
分配:
Interface{} 版本的分配量增加了 3 倍,這主要是由於插入和檢索值時的裝箱/拆箱。這增加了垃圾收集的開銷。
對於較大的工作負載,例如 100 萬次入隊/出隊操作,效能差距會擴大。具有高吞吐量要求的實際應用程式(例如訊息佇列、作業排程器)可以從泛型中受益匪淺。
Go 中的泛型在功能強大和簡單性之間取得了平衡,為編寫可重複使用和類型安全的程式碼提供了實用的解決方案。雖然不像 Rust 或 C 那樣功能豐富,但與 Go 的極簡主義哲學完美契合。了解約束等約束。有效有序和利用泛型可以大幅提高程式碼品質和可維護性。
隨著泛型的不斷發展,它們注定會在 Go 的生態系統中發揮核心作用。因此,潛入、實驗並擁抱 Go 程式設計類型安全性和靈活性的新時代!
查看 github 儲存庫以取得一些關於泛型的範例。
歡迎來到Go 泛型儲存庫!此儲存庫是 1.18 版本中引入的用於理解、學習和掌握 Go 中泛型的一站式資源。泛型為 Go 帶來了類型參數的強大功能,使開發人員能夠編寫可重複使用且類型安全的程式碼,而不會影響效能或可讀性。
該儲存庫包含精心策劃的範例,涵蓋廣泛的主題,從基本語法到高級模式和實際用例。無論您是初學者還是經驗豐富的 Go 開發人員,此集合都將幫助您在專案中有效地利用泛型。
這些範例介紹了泛型的基本概念,幫助您掌握文法和核心功能:
以上是Go 中的泛型:改變程式碼可重複使用性的詳細內容。更多資訊請關注PHP中文網其他相關文章!