首頁 > 後端開發 > Golang > Go 中的泛型:改變程式碼可重複使用性

Go 中的泛型:改變程式碼可重複使用性

Patricia Arquette
發布: 2025-01-08 06:20:41
原創
523 人瀏覽過

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: 宣告函數名稱 Su​​m

[T int | float64]: 指定引入T 作為類型參數的類型參數列表,約束為特定類型(int 或float64 )。 Sum 函數只能採用 int 或 float64 參數,不能組合使用,兩者都必須是 intfloat64。我們將在下面的部分中進一步探討這一點。

(a, b T): 宣告兩個參數a 和b,皆為T 類型(泛型型別) ).

T: 指定函數的回傳類型,與型別參數 T.

相符

約束:泛型的建構塊

約束定義哪些操作對於泛型類型有效。 Go 提供了強大的約束工具,包括實驗性約束套件(golang.org/x/exp/constraints)。

內建約束

Go 引入了具有泛型的內建約束,以提供類型安全,同時允許靈活地定義可重複使用和泛型程式碼。這些約束使開發人員能夠對泛型函數或類型中使用的類型強制執行規則。

Go 有以下內建約束

  1. any:代表任意型別。它是 interface{} 的別名。當不需要約束時使用
func FunctionName[T TypeConstraint](parameterName T) ReturnType {
    // Function body using T
}
登入後複製
登入後複製
登入後複製
  1. comparable:允許支援相等比較(== 和 !=)的型別。對於映射鍵、重複檢測或相等檢查很有用。這不能用於映射、切片和函數,因為這些類型不支援直接比較。
func Sum[T int | float64](a, b T) T {
    return a + b
}
登入後複製
登入後複製
登入後複製

實驗限制

  1. constraints.Complex:允許複雜數字類型(complex64 和complex128)。
  2. constraints.Float:允許浮點數值型別(float32 和 float64)
  3. constraints.Integer:允許任何有符號和無符號整數(int8、int16、int32、int64、int、uint8、uint16、uint32、uint64 和 uint)
  4. constraints.Signed:允許任何有符號整數(int8、int16、int32、int64 和 int)
  5. constraints.Unsigned:允許任何無符號整數(uint8、uint16、uint32、uint64 和 uint)。
  6. constraint.Ordered:允許進行比較的類型(<.>、>=),支援所有數字類型和字串(int、float64、string 等)。
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{}來實現靈活性。然而,這種方法有其限制。

類型安全

  • interface{}:依賴執行階段類型斷言,增加執行時間出錯的幾率。

  • 泛型:提供編譯時類型安全性,在開發過程中儘早捕捉錯誤。

表現

  • 介面{}:由於額外的執行時間類型檢查,速度較慢。

  • 泛型:更快,因為編譯器會產生特定於類型的最佳化程式碼路徑。

程式碼可讀性

  • 介面{}:通常冗長且不太直觀,使程式碼更難維護。

  • 泛型:更簡潔的語法可以帶來更直覺和可維護的程式碼。

二進位大小

  • 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
}
登入後複製
登入後複製
登入後複製

泛型消除了由不正確的類型斷言引起的運行時恐慌的風險並提高了清晰度。

表現

泛型為每種類型產生專門的程式碼,與介面{}相比,可以帶來更好的執行時間效能。

二進位大小

存在一個權衡:泛型由於每種類型的程式碼重複而增加了二進位大小,但這與好處相比通常可以忽略不計。

Go 泛型的局限性

約束的複雜性: 雖然像 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 儲存庫以取得一些關於泛型的範例。

GitHub logo 薩達南多達瓦達卡爾 / Go泛型

儲存庫包含 go 泛型的工作範例

Go 泛型:綜合範例儲存庫

歡迎來到Go 泛型儲存庫!此儲存庫是 1.18 版本中引入的用於理解、學習和掌握 Go 中泛型的一站式資源。泛型為 Go 帶來了類型參數的強大功能,使開發人員能夠編寫可重複使用且類型安全的程式碼,而不會影響效能或可讀性。

該儲存庫包含精心策劃的範例,涵蓋廣泛的主題,從基本語法到高級模式和實際用例。無論您是初學者還是經驗豐富的 Go 開發人員,此集合都將幫助您在專案中有效地利用泛型。


?裡面有什麼

?基本通用程式

這些範例介紹了泛型的基本概念,幫助您掌握文法和核心功能:

  1. GenericMap:示範通用映射函數來轉換任何類型的切片。
  2. 交換:一般交換兩個值的簡單而強大的範例。
  3. FilterSlice:顯示如何過濾...


在 GitHub 上查看


以上是Go 中的泛型:改變程式碼可重複使用性的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板