Go 1.28 ジェネリック・コレクション・タイプ提案

Goは、Go 1.28において、これまで欠落していた、あるいは回避策を通じて実装されていた標準的で使いやすいデータ構造を提供するために、包括的なジェネリック・コレクション・タイプのセットを導入します。この動きは、Go 1.18でのジェネリクスの追加とGo 1.23でのイテレータの追加を活用し、Goの核心的な原則である実用性とシンプルさを維持しながら、一般的なデータ構造を標準ライブラリに取り込むものです。

New Standard Collection Types

Go Collections ワーキンググループは、開発者がセット、マップ、ヒープを扱う方法を標準化するために、いくつかの新しいパッケージと型を提案しています。

Hash-Based Collections

  • container/hash.Map[K, V]: hash/maphash.Hasher インターフェースを介してカスタムハッシュ関数と等価関係をサポートする、ハッシュベースのマップです。これは、比較可能ではないキー型(例:スライスやマップ)や、深い比較が必要な場合に特に有用です。
  • 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]: 範囲クエリが必要なユースケース向けに設計された、順序付きマップの実装(現在はバランス二分木を使用)です。マップのキーを手動でソートする一般的なパターンのパフォーマンスを上回ります。
  • container/heap/v2.Heap: 広く使いにくいと考えられている既存の container/heap 実装を置き換えるために設計された、ジェネリックな二分ヒープ API です。

Helper Packages

  • container/mapset: 既存のコードへの API 変更を必要とせずに、「レガシー」なセット(既存の map[T]struct{} コード)を操作するためのヘルパー関数のパッケージ(Union, Intersection, など)です。

Abstract Collection Constraints

異なるコレクションの実装間で一貫性を確保するため、Go チームは未公開の抽象制約インターフェースを導入しました。これらのインターフェースは、F-bounded polymorphism(再帰的制約インターフェース)を使用して、「バイナリメソッド問題」を解決します。これは、Union(S) S のようなメソッドが、演算対象と結果の両方に同じ具体的なセット型が使用されることを保証しなければならないという問題です。

内部の階層は以下の通りです:

  • _AbstractCollection[E, C]: Clear(), Clone(), Contains(E), および Len() のような基本操作を定義します。
  • _AbstractMap[K, V, M]: コレクション・インターフェースを、Get(K), Set(K, V), および Keys() のようなマップ特有の操作で拡張します。
  • _AbstractSet[E, S]: コレクション・インターフェースを、Union(S), Intersection(S), および SymmetricDifference(S) を含むセット特有の操作で拡張します。

これらのインターフェースは、現在、公開 API を決定する前に Go チームが実践から学ぶことを可能にするために未公開とされていますが、標準ライブラリ全体の一貫性のための設計図として機能します。

Design Rationales and API Conventions

新しいコレクション API は、パフォーマンスと使いやすさを最適化するために、いくつかの特定の設計上の選択を行っています:

  • Information-Rich Returns: 変更操作(Mutation)メソッドは、コレクションのサイズが変更されたかどうかを報告します。Map.SetMap.Delete は、以前の値と、既存のキーとゼロ値とを区別するための boolean を返します。
  • -With Variants: セット代数操作は、2つの形式で提供されます:新しいセットを返す純粋関数的なバージョン(例: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."

他の開発者は、、Go のこれらの機能の採用が遅いことは、業界標準への到着が遅いというパターンを反映していると主張しています:

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

また、コミュニティの一部には、ジェネリクスや複雑なコレクション・タイプの追加が、Go の本来のアイデンティティである「貧しいプログラマー」のためのシンプルさから Go を遠ざけるのではないかという懸念を表明する人々もいます。一部からは、これらの変更がライブラリ作者には利益をもたらすが、平均的な開発者にとっては言語をより複雑にするのではないかと示唆されています。

Sources