golang-set

A generic, thread-safe set collection for Go modeled after Python's set type.

Library
Go
vv2.9.0
4,701stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
54/100Fair
Development Activity24
Maintenance24
Community68
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
68/100Good
Architecture78
Code Quality82
Innovation45
Learning Curve65

golang-set fills a gap in Go’s standard library by providing a full-featured set collection, the kind of data structure Python developers take for granted. Built on Go generics (Go 1.18+), it lets you create a Set[T] for any comparable type — ints, strings, structs, pointers — without falling back to map[T]struct{} boilerplate scattered across a codebase.

The package ships two interchangeable implementations behind one Set[T] interface: a thread-safe variant guarded by a sync.RWMutex for concurrent access, and a thread-unsafe variant that trades safety for raw performance in single-goroutine code. Both support the complete set algebra — union, intersect, difference, symmetric difference, subset/superset checks — plus channel- and callback-based iteration, JSON and BSON marshaling, and convenience methods like Pop, PopN, and Filter.

It’s old enough to have working solutions for the edge cases (deadlock-safe Each, escape-analysis-aware Contains) and popular enough that companies like Docker, 1Password, Ethereum, and Hashicorp depend on it in production.

What You Get

  • Generic Set[T] interface - one API surface for any comparable type, backed by either a thread-safe or thread-unsafe implementation you choose at construction time.
  • Full set algebra - Union, Intersect, Difference, SymmetricDifference, IsSubset/IsSuperset/IsProperSubset/IsProperSuperset, and Equal, all implemented for both variants.
  • Flexible iteration - a blocking Each callback, a <-chan T via Iter(), and a stoppable Iterator[T] via Iterator() for early-exit ranging.
  • Serialization support - built-in MarshalJSON/UnmarshalJSON and MarshalBSONValue/UnmarshalBSONValue so sets round-trip cleanly through APIs and MongoDB documents.
  • Bulk and capacity-aware constructors - NewSetWithSize, Append, AppendFrom, PopN, and NewSetFromMapKeys reduce allocations and boilerplate for common bulk operations.

Common Use Cases

  • Deduplicating collections - collapsing a slice of IDs, tags, or events down to unique members without hand-rolling a map[T]struct{}.
  • Membership and containment checks - fast Contains/ContainsAny/ContainsAnyElement lookups in place of linear slice scans.
  • Set-based diffing - computing what changed between two snapshots (added/removed/common elements) using Difference and Intersect.
  • Concurrent shared state - using the thread-safe variant as a safely shared registry of active workers, connections, or feature flags across goroutines.
  • Config and permission modeling - representing required vs. granted capabilities as sets and checking IsSubset/IsSuperset relationships between them.

Under The Hood

Architecture golang-set is a small, flat package (mapset) with no internal layering: a public Set[T] interface in set.go is implemented twice — threadSafeSet[T] in threadsafe.go wraps a sync.RWMutex around an embedded threadUnsafeSet[T], and threadUnsafeSet[T] in threadunsafe.go is a bare map[T]struct{} with methods hung directly off the map type. Every threadsafe method locks, delegates to the equivalent unsafe method, and unlocks, so the two implementations stay behaviorally identical by construction rather than by duplicated logic. Cross-set operations (Union, Intersect, etc.) type-assert the argument back to the concrete implementation, which means a threadsafe set and a thread-unsafe set cannot be combined directly — a deliberate constraint that keeps the locking model unambiguous. A separate iterator.go supplies a channel-based Iterator[T] with a stop channel for early termination, used identically by both implementations.

Tech Stack The module targets Go 1.18+ to use generics and has exactly one runtime dependency, go.mongodb.org/mongo-driver (pinned to v1.17.9 in go.sum), pulled in solely for its bson/bsontype packages to support MarshalBSONValue/UnmarshalBSONValue. A build-tag-gated helper.go/helper_1_21.go pair swaps between a hand-rolled mapclone and the standard library’s maps.Clone depending on whether the toolchain is pre- or post-Go 1.21. CI runs the test suite with -race across three Go versions (1.24-1.26) and three operating systems (Ubuntu, macOS, Windows) via GitHub Actions, giving reasonable confidence that the concurrency guarantees hold across platforms.

Code Quality The project is thoroughly tested — roughly 3,700 lines of test code (set_test.go, threadsafe_test.go, threadunsafe_test.go, sorted_test.go, plus a runnable example and an extensive benchmark suite) against under 1,000 lines of implementation, and every test run is race-detected in CI. Error handling is minimal because the API is designed to avoid error returns in favor of boolean/ok results (Add returns a bool, Pop/PopN return an ok flag), which is idiomatic for a collection type. Naming is consistent and every exported method carries a doc comment, including notes on panic behavior when set types are mismatched. No linter config or go vet step is currently wired into CI (it’s commented out in the workflow), which is the one gap in an otherwise disciplined setup.

What Makes It Unique The library’s core value is filling a real absence: Go has no built-in set type, and golang-set was one of the earliest and is now one of the most widely adopted generic implementations of one, with adoption from large infrastructure projects. It doesn’t attempt anything algorithmically novel — it’s a straightforward map-backed set — but its dual thread-safe/unsafe design behind one interface, its long production track record, and its BSON support for MongoDB-heavy Go codebases distinguish it from smaller or newer alternatives in the same space.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search