Go 1.28 泛型集合類型提案

Go 在 1.28 版本中將引入一套全面的泛型集合類型,以提供先前缺失或需透過權宜做法實現的標準且符合人體工學的資料結構。此舉利用了 Go 1.18 中泛型的加入以及 Go 1.23 中的迭代器,將常見的資料結構引入標準函式庫,同時維持 Go 務實與簡單的核心原則。

New Standard Collection Types

Go Collections 工作小組提議了幾個新套件與類型,以標準化開發者處理集合 (sets)、映射 (maps) 與堆積 (heaps) 的方式。

Hash-Based Collections

  • container/hash.Map[K, V]: 一個基於雜湊的映射,透過 hash/maphash.Hasher 介面支援自定義雜湊函數與等價關係。這對於不可比較 (例如 slices 或 maps) 或需要深度比較之鍵值類型特別有用。
  • container/hash.Set[T]: 一個基於雜湊的集合實現,遵循與 container/hash.Map 相同的自定義雜湊原則。
  • container/set.Set[T]: 針對可比較元素的標準集合,透明地以 map[T]struct{} 表示。它提供標準的集合運算,如 Union 與 Intersection,旨在取代舊有的 map[T]boolmap[T]struct{} 模式。

Ordered and Specialized Collections

  • container/ordered.Map[K, V]: 一個有序映射實現 (目前使用平衡二元樹),專為需要範圍查詢的使用案例設計,其效能優於手動對 map 鍵值進行排序的常見模式。
  • container/heap/v2.Heap: 一個泛型二元堆積 API,旨在取代現有的 container/heap 實現,後者被廣泛認為使用起來較困難。

Helper Packages

  • container/mapset: 一個包含輔助函數 (Union, Intersection, 等) 的套件,旨在操作「舊有」集合 (現有的 map[T]struct{} 程式碼) 而無需對現有程式碼進行 API 變更。

Abstract Collection Constraints

為了確保不同集合實現之間的一致性,Go 團隊引入了未公開的抽象約束介面。這些介面使用 F-bounded polymorphism (遞迴約束介面) 來解決「二元方法問題」(binary method problem),即如 Union(S) S 之類的方法必須確保運算元與結果使用相同的具體集合類型。

內部層級結構包括:

  • _AbstractCollection[E, C]: 定義了基本操作,如 Clear(), Clone(), Contains(E), 與 Len()
  • _AbstractMap[K, V, M]: 擴展了集合介面,包含 map 專用的操作,如 Get(K), Set(K, V), 與 Keys()
  • _AbstractSet[E, S]: 擴展了集合介面,包含 set 專用的操作,如 Union(S), Intersection(S), 與 SymmetricDifference(S)

雖然這些介面目前尚未公開,以便讓 Go 團隊在投入公用 API 之前先從實踐中學習,但它們作為標準函式庫一致性的藍圖。

Design Rationales and API Conventions

新的集合 API 遵循幾項特定的設計選擇,以優化效能與人體工學:

  • 資訊豐富的傳回值: 變更方法會回報是否改變了集合的大小。Map.SetMap.Delete 會回傳先前的值,並透過一個布林值來區分現有鍵值與零值。
  • -With Variants: 集合代數運算提供兩種形式:純函數式版本 (例如 Union) 會回傳一個新集合,而變更版本 (例如 UnionWith) 會修改左側運算元以避免不必要的配置。
  • 漸近效能 (Asymptotic Performance): 某些操作如 DeleteFunc 被保留在介面中,因為它們的專用化實現 (例如在基於樹的映射中) 在漸近效能上比通用的基於迴圈的實現更有效率 (O(n) vs O(n log n))。

Community Perspectives

社群對此提案的反應褒貶不一,反映了關於 Go 的演進長期以來的爭論。

部分開發者認為這些新增內容是遲來的現代化:

"Stuff like sets or a typed heap is long overdue."

其他人則認為,語言對這些功能的採用速度緩慢,反映了其在產業標準上的遲到模式:

"Step by step, Go is now learning the hard lessons every other language has learned over the last 20 years."

也有部分社群成員表示擔憂,認為加入泛型與複雜的集合類型會使 Go 偏離其原本為「不擅長程式設計的人」提供簡單性的身分,有人建議這些變來會使語言對一般開發者而言變得更複雜,但對函式庫作者而言卻有利。

Sources