#80590active
container/...: generic collection types
新規提案
要約
AIによる要約であり、誤りを含む場合があります。
概要
Go 1.18 のジェネリクスと Go 1.23 のイテレータを土台に、セット、ハッシュマップ、ソート済みマップ、ヒープといった汎用コレクション型をまとめて導入するための「アンブレラ(傘)proposal」です。2025年末に発足した Go Collections ワーキンググループ(@jba, @adonovan, @griesemer, @mvdan, @rogpeppe, @khr, @ianlancetaylor)が提出しました。個々の型は別の proposal で扱い、この issue では全体方針と API の共通規約を議論します。
ステータス変更
(新規) → active
2026年10月7日のレビュー会議で、@aclements が次の3点を検討事項として挙げ、Active 列に追加されました。
- そもそもこれをやるべきか(「ほぼ確実に yes」とのこと)
- パッケージをどこに置くか(まずは
x/exp/collectionが良さそう) - API の大枠は妥当か
当初は Go 1.28 で標準ライブラリに入れる案でしたが、コミュニティの意見を受けて方針が変わりました。まずgolang.org/x/expで実際に使ってもらい、約半年後(Go 1.29 の時期)に標準ライブラリ入りを改めて提案します。この変更で受け入れのハードルが下がったことが、Active 化の後押しになったと考えられます。
技術的背景
現状の問題点
Go 標準のコレクションは事実上スライスと map だけです。セットすらなく、map[T]bool か map[T]struct{} で代用するのが慣習でした。map[T]bool では「false の値が入っている」ケースが紛らわしくなります。ほかにも次のような不足がありました。
- 二分木などで実装された順序付きマップがない
- スライスや
types.Typeのような比較できないキー、または独自の等価判定が必要なキーを扱えるハッシュマップがない - 既存の
container/heapは使いにくい
提案された解決策
個別 proposal として次の型が挙げられています(パッケージ名は議論中)。
hash/maphash.Hasher(#70471、Go 1.27 で導入済み): 独自のハッシュ関数と等価関係を表すインターフェースhashed.Map[K,V](#69559)とhashed.Set[T](#80584): Hasher を使うハッシュテーブルset.Set[T](#69230): 中身はmap[T]struct{}そのもので、Union や Intersection などの集合演算を持つ「標準のセット」sorted.Map[K,V]とsorted.Set(#60630): キー順に並ぶマップとセット。実装はバランス木ですが、名前で実装を縛らない方針ですheap.Heap(#77397): 型パラメータを持つヒープ。既存の heap を置き換えます
全型に共通する API 規約もあります。- 値の取得は
Get(K) VとLookup(K) (V, bool) SetとDeleteは以前の値と存在フラグを返す- 集合演算には、新しいセットを返す
Unionと、左辺を書き換えるUnionWithの2種類を用意する
共通の抽象インターフェース(F-bounded な制約型、つまり自分自身の型を型パラメータに取る制約)は非公開のままで、今回の proposal には含まれません。
これによって何ができるようになるか
- 重複除去や権限チェックなどで、型安全なセットを集合演算付きで使える
- 範囲検索やソート順での走査が必要な場面(時系列インデックス、リーダーボードなど)で、毎回キーをソートせずに済む
go/typesの型のように「同一性を独自に定義するキー」を使ってハッシュマップを作れる- 型パラメータを持つヒープで、優先度付きキューを簡単に書ける
コード例
// Before: map[T]struct{} で集合演算を手書きする
a := map[string]struct{}{"read": {}, "write": {}}
b := map[string]struct{}{"write": {}, "admin": {}}
union := make(map[string]struct{})
for k := range a { union[k] = struct{}{} }
for k := range b { union[k] = struct{}{} }
// After: set.Set を使う(API は x/exp での検証中のため、今後変わる可能性があります)
a := set.Set[string]{}
a.Insert("read") // 要素数が変わったかを bool で返す
u := a.Union(b) // 新しいセットを返す
a.UnionWith(b) // a を書き換える(アロケーションを抑えられる)
// 順序付きマップ
m := sorted.Map[int, string]{} // 生成方法は個別 proposal で議論中
m.Set(3, "c")
v, ok := m.Lookup(3)
議論のハイライト
- パッケージ名で最も議論が分かれた。
container/{sets,maps}案は既存のmapsパッケージと名前が衝突するとして @neild らが強く反対しました。@jba が Rust、C++、Java、Swift の命名を比較したうえでhashed.Map/sorted.Map案に戻しています。また、Linux のコンテナと紛らわしいことと、既存のcontainer/*と区別するため、親パッケージ名はcollectionにする方向です。 - 変更の有無を返す bool の扱い。 @aclements は、遅延評価型や並行データ構造ではこの bool を返しにくいと懸念しました。ワーキンググループは「API はメモリ上で即時に処理する素直な実装向けに設計する」と判断しています。
- 削除されたメソッドと名前の変更。
ContainsAll/DeleteAll/SetAllは意味が曖昧なうえ、ループで書けるため削除されました。代わりにイテレータ側のメソッドを別途提案しています(#81382)。At/GetはそれぞれGet/Lookupに改名されました。 - 抽象インターフェースは公開しない。 Java のような統一的なインターフェース体系は目指さず、実際の利用経験を積んでから公開するかどうかを決めます。マップを「ペアのコレクション」とみなす案も見送られました。
- JSON 対応は未決。 マップは
encoding/jsonの map と同じ扱いにし、セットは JSON 配列にする案が出ています。@adonovan は「後から形式を変えると多くのプロジェクトが壊れるので早く決めるべき」と指摘しました。@Merovius は、順序のないセットを JSON 配列に出すと出力が実行ごとに変わり得る点を挙げています。