Golang proposal: container/: generic collection types

Aug 01, 2026 01:39 AM - 2 hours ago 2

Background: The Go Collections moving group was formed successful precocious 2025 pinch the intent of bringing communal postulation information structures to the modular library, guided by the acquainted Go principles of pragmatism and simplicity. Alphabetically by past name, the group consists of Jonathan Amsterdam (@jba), Alan Donovan (@adonovan), Robert Griesemer (@griesemer), Daniel Martí (@mvdan), Roger Peppe (@rogpeppe), Keith Randall (@khr), and Ian Lance Taylor (@ianlancetaylor). We’ve now reached a constituent wherever we’re fresh to stock our results pinch the community.

This rumor is an umbrella for discussing respective related proposals for caller collections APIs for Go 1.28. It presents a precocious level overview of the themes, and links to the various actual proposals and associated implementation CLs.

Go presently provides fewer postulation types successful its library, and from the outset we person emphasized the elasticity of the language’s built-in portion and representation types. Of those provided, the astir important is the heap, utilized for privilege queues. Even sets are absent; they are conventionally expressed successful position of map[T]bool aliases map[T]struct{}. Ordered maps and sets based connected binary trees are wholly absent.

Since the summation of generics successful Go 1.18 and iterators successful Go 1.23, it has go imaginable for library-defined types to execute comparable ergonomics to built-in types, and for galore communal operations connected slices and maps to beryllium expressed arsenic calls to room functions. This activity seeks to adhd respective of the much important information types to the modular library, and to found conventions for their APIs and those of early additions.

Proposal: The projected additions include:

  • #70471, CL 657296 (released successful go1.27): hash/maphash.Hasher: a modular interface for expressing civilization hash functions and equivalence relations for arbitrary information types. These whitethorn disagree from the compiler-defined ones utilized by map[K]V, and are useful erstwhile the cardinal type is not comparable (such arsenic a portion aliases map), aliases erstwhile the default comparison yields the incorrect consequence (such arsenic for types.Type values, which request the heavy comparison cognition types.Identical). Its package docs see an illustration of its usage successful a Bloom filter.

  • #69559, CL 612217: container/hash.Map[K,V]: a hash-based Map that uses the civilization hash functions mentioned above.

  • #80584, CL 741160: container/hash.Set[T]: a hash-based Set on the aforesaid lines.

  • #69230, CL 745441: container/set.Set[T]: a canonical information type for sets whose elements are comparable. It is transparently represented arsenic map[T]struct{} and supports each the accustomed group operations specified arsenic Union and Intersection. It is much convenient than “legacy” sets based connected map[T]bool and map[T]struct{}, and avoids ambiguity astir imaginable mendacious values successful a map[T]bool. We expect it to go the modular group successful astir caller Go APIs.

  • #77052, CL 724420: container/mapset: a package of helper functions (Union, Intersection, and truthful on) for conveniently manipulating bequest sets arsenic sets successful existing codification whose API cannot beryllium changed. These functions are precisely parallel to the methods of set.Set.

  • #60630: container/ordered.Map[K,V]: an ordered mapping. The existent implementation uses a balanced binary tree, but thing successful the creation requires that. The communal Go shape of building a map[K]V past sorting its keys performs good successful astir cases, but connected occasion, specified arsenic erstwhile a scope query is needed, different information structures execute overmuch better.

  • #77397: container/heap/v2.Heap: a generic binary heap API to switch the modular library's existing heap, which tin beryllium difficult to use.

We expect to see further proposals successful owed course, specified arsenic insertion-ordered hash maps (#80194) and stacks.

The first implementations of each the projected information structures purpose to fulfill the API and asymptotic capacity expectations arsenic simply arsenic possible. There are doubtless galore opportunities for later optimizations to trim changeless factors, but they are retired of scope of the connection process.

Though the caller packages will unrecorded successful the existing instrumentality tree, we for illustration the word “collection” to debar disorder pinch the instrumentality virtualization conception from Linux.

Abstract postulation constraint interfaces

Most of the methods of the caller Map and Set types are not peculiar to immoderate actual practice type, but are communal crossed each Maps and Sets. However, they are not really implementions of a communal interface type because of the “binary method problem”: if each group information type S has a Union method of the shape func (S) Union(S) S, past the Union methods of different group types are incompatible, truthful they person nary communal mean interface. To definitive this absurd Set type, we must usage F-bounded polymorphism, aliases recursive constraint interfaces.

CL 761460 adds to the container package unexported absurd Collection, Set, and Map constraint interface types that licence package implementors to constitute absurd helper functions (such arsenic ContainsAny, Subset, aliases Arbitrary) that activity crossed a scope of actual collection, group aliases representation types. We reproduce these interfaces below, pinch immoderate little commentary, to thief springiness a high-level image but they are not portion of immoderate proposal. They simply service to guarantee conformance successful tests. See the individual proposals for much detail.

// _AbstractCollection models a postulation C of elements E, // specified arsenic *hash.Map, *hash.Set, *ordered.Map, aliases set.Set. type _AbstractCollection[E any, C _AbstractCollection[E, C]] interface { Clear() Clone() C Contains(E) bool ContainsAll(iter.Seq[E]) bool Len() int String() string } // _AbstractMap models a mapping M from keys K to values V, // specified arsenic *hash.Map aliases *ordered.Map. type _AbstractMap[K, V any, M _AbstractMap[K, V, M]] interface { _AbstractCollection[K, M] All() iter.Seq2[K, V] At(K) V Delete(K) (V, bool) DeleteAll(iter.Seq[K]) bool DeleteFunc(func(K, V) bool) bool Get(K) (V, bool) Keys() iter.Seq[K] Set(K, V) (V, bool) SetAll(iter.Seq2[K, V]) bool Values() iter.Seq[V] } // _AbstractSet models a group S of elements E, // specified arsenic *hash.Set, aliases set.Set. type _AbstractSet[E any, S _AbstractSet[E, S]] interface { _AbstractCollection[E, S] All() iter.Seq[E] Delete(E) bool DeleteAll(iter.Seq[E]) bool DeleteFunc(func(E) bool) bool Difference(S) S DifferenceWith(S) Equal(S) bool Insert(E) bool InsertAll(iter.Seq[E]) bool Intersection(S) S IntersectionWith(S) Intersects(S) bool SymmetricDifference(S) S SymmetricDifferenceWith(S) Union(S) S UnionWith(S) }

For now these absurd types are non-exported and simply service arsenic archiving of Go’s conventions to thief guarantee consistency. We do not propose to people them yet, but whitethorn do truthful a later merchandise aft gaining acquisition pinch the actual postulation types. In the meantime, users tin specify minimal constraint types arsenic needed, arsenic successful this illustration (from CL 761460) of a generic Take usability complete absurd sets:

// _TakeSet defines an abstraction of a group capable for the [Take] function. type _TakeSet[E any, S _TakeSet[E, S]] interface { All() iter.Seq[E] Delete(E) bool } // Take removes and returns an arbitrary constituent from a set. // It returns zero if the group was empty. func Take[S _TakeSet[E, S], E any](set S) (e E, found bool) { for e = range set.All() { found = true set.Delete(e) break } return }

There is simply a definite arbitrariness to the group of methods included successful each interface. For immoderate information structures, a method permits a much businesslike specialized implementation. But if each imaginable cognition were added to the interface, the load connected the implementor would beryllium unreasonable.

For instance, should the Set interface see a Subset(Set) bool method, aliases should Subset beryllium written arsenic a generic cognition complete absurd sets, for illustration the Take example? Ordered sets tin quickly cull a Subset trial erstwhile the 2 operands person disjoint ranges, but moreover so, successful the communal lawsuit a Subset trial is still typically O(n), truthful we decided to omit Subset from the interface. By contrast, we retained DeleteFunc successful the Set and Map interfaces because, without it, conditionally deleting each constituent of a character is asymptotically worse: O(n log n) alternatively of O(n).

By delaying the committedness to a peculiar group of methods, we tin study from practice. It whitethorn move retired that we don’t request to people canonical constraint types astatine all.

Miscellaneous rationalizations

The remainder of this doc concisely notes a fewer of the galore mini creation choices that led to the existent group of proposals.

Methods return arsenic overmuch accusation arsenic imaginable to debar repeated lookups. For example:

  • Most mutation methods study whether they changed the size of the collection.
  • Map.Set and Map.Delete return the erstwhile cardinal if any, positive a boolean truthful that an existing cardinal tin beryllium distinguished from the zero value.
  • Get is version of At that provides the boolean. (At is provided for convenience of usage successful expressions.)

Map.Set should switch immoderate existing introduction pinch the aforesaid key, pursuing the built-in map.

Unlike Sets, maps person nary Equal method, because representation values whitethorn beryllium non-comparable.

The basal group operations (Intersects, Union, etc) are portion of the Set interface to alteration businesslike actual implementations crossed a assortment of representations, moreover though galore of these could beryllium expressed abstractly successful position of conscionable All, Len, and Contains, arsenic shown successful this table, astatine immoderate asymptotic costs successful performance:

- Union{,With} interface{ All() iter.Seq\[E\] } - Intersection interface{ All() iter.Seq\[E\]; Contains(E) bool; Len() int } - IntersectionWith interface{ Contains(E) bool } - Difference interface{ All() iter.Seq\[E\]; Contains(E) bool } - DifferenceWith interface{ All() iter.Seq\[E\]; Contains(E) bool } - SymmetricDifference{,With} interface{ All() iter.Seq\[E\] }

Secondary operations specified arsenic Set.{Take,Arbitrary,Subset,Superset} were removed from the interface and expressed arsenic generic operations utilizing the absurd group interface, again astatine immoderate imaginable costs successful asymptotic performance. DeleteFunc was retained.

Set algebra operations specified arsenic Union are purely functional, returning their consequence arsenic a caller set. Each comes pinch a -With version that mutates its near operand and returns nary result. The 2 variants are “convenient” and “allocation efficient”, respectively. We rejected the thought of merging them into a azygous method based connected acquisition pinch the math/big.Int API, and to debar risks of accidental mutation aliases forgetting to usage the result.

It is imaginable to specify a KeySetView[M, K, V] wrapper type that satisfies the Set[K] abstraction utilizing the cardinal group of an underlying Map[K,V] of type M. (Inserting an constituent to the Set is of people meaningless and must panic.)

For symmetry, let’s see each of the AbstractMap methods and the operations provided by the existing ‘maps’ package:

  • no maps.Clear: served by builtin clear(m)
  • AbstractMap.Clone = maps.Clone
  • no maps.Contains; served by _, good = m[k]; but spot connection #67377
  • no maps.ContainsAll: served efficiently by for k := scope seq { _, good = m[k], … }
  • no maps.Len: served by builtin len(m)
  • AbstractMaps.All = maps.All
  • no maps.At: served by m[k]
  • no maps.Delete: served by delete(m, k)
  • no maps.DeleteAll: served efficiently by for k = scope seq { delete(m, k) }
  • AbstractMaps.DeleteFunc = maps.DeleteFunc
  • no maps.Get: served by v, good = m[k]
  • AbstractMaps.Keys = maps.Keys
  • no maps.Set: served by prev, good = m[k]; m[k] = newval
  • AbstractMap.SetAll = maps.Insert
  • AbstractMaps.Values = maps.Values

Five of them (Clone, DeleteFunc, All, Keys, Values) are precisely parallel. One cognition (AbstractMap.SetAll) has a different sanction (maps.Insert). All the remainder are served by built-in operators.
We mightiness want to propose adding maps.{Contains,ContainsAll,DeleteAll}. Contains is much useful than _, good = s[k] successful an look context; ContainsAll and DeleteAll debar the request for loops and boolean bookkeeping.

More