Menu

Golang Set: map[T]struct{}, Operations and a Generic Set

Go has no built-in set type. The standard idiom is a map with empty struct values. Learn add, has and remove, union, intersection and difference, and how to write a small generic Set.

This page includes runnable editors - edit, run, and see output instantly.

The idiom: a map with empty values

Go has no set keyword and no set in the standard library. A map whose keys are the elements and whose values carry nothing does the job.

struct{} is the empty struct type and struct{}{} is its only value. It occupies zero bytes, so the map stores keys and nothing else.

All the map rules apply: keys must be comparable, iteration order is random, a nil map panics on write, and concurrent writes need a lock. The maps page covers each one.

map[T]struct{} or map[T]bool

The other common spelling is map[T]bool. It reads better because a missing key returns false:

seen := map[string]bool{}
seen["a"] = true
if seen["a"] { ... }

The trade-offs:

map[T]struct{}map[T]bool
Value size0 bytes1 byte (plus alignment)
Membership test_, ok := s[k]s[k]
Ambiguitynones[k] = false is a third state

The third row is the real reason many codebases prefer the empty struct: with bool, someone eventually writes s[k] = false and len(s) stops being the number of members. For small sets the memory difference does not matter.

Removing duplicates from a slice

The most common use of a set is deduplication. This keeps the first occurrence of each value and preserves order:

Output:

[b a c]
[3 1 2]
[1 2 3]

Union, intersection, difference

Set algebra is a few loops. Iterate the smaller set when testing membership in the other, since each lookup is constant time on average.

sorted exists only to make the output stable. Printing a set by ranging over it gives a different order on each run. maps.Keys and slices.Sorted need Go 1.23.

A named type like type set map[string]struct{} is still a map: you index it, range it and delete from it the same way, and you can hang methods on it.

A small generic Set

With generics (Go 1.18), one type covers every comparable element type. This is enough for most programs:

Wrapping the map in a struct hides the struct{}{} noise and guarantees the map is created by the constructor, which removes the nil map panic. Sorted cannot be a method: a method cannot declare its own type parameters or tighten the type's comparable constraint, and sorting needs cmp.Ordered. The generics page explains constraints.

If you need a full-featured set (thread-safe variants, many operations), third-party packages such as github.com/deckarep/golang-set exist. For most code, the map idiom or a 30-line type like this one is what Go programmers use.

Sets of structs

Any comparable type can be an element, including structs with comparable fields. That makes "have I seen this pair" checks direct:

type edge struct{ from, to string }
visited := map[edge]struct{}{}
visited[edge{"a", "b"}] = struct{}{}

Slices and maps cannot be set elements. To track unique slices, convert each one to a comparable key first, for example an array of fixed size, or a string built with fmt.Sprint.

Common mistakes

  • Forgetting to initialize. var s map[string]struct{} is nil; the first add panics.
  • Printing a set and expecting stable output. Sort the members first.
  • Using map[T]bool and storing false. Then len no longer counts members. Use delete to remove.

Frequently Asked Questions

Does Go have a set type?

No. The standard library has no set. The idiom is a map whose values carry no information: map[string]struct{}. Adding is s[k] = struct{}{}, membership is _, ok := s[k], removal is delete(s, k), and the size is len(s).

Should I use map[T]bool or map[T]struct{} for a set in Go?

map[T]struct{} makes intent explicit and its values take zero bytes. map[T]bool reads more naturally (if seen[x]) because a missing key returns false. Both are correct; the memory difference only matters for very large sets. Pick one and stay consistent.

How do I remove duplicates from a slice in Go?

To keep the first occurrence in order, loop and track seen values in a map[T]struct{}, appending only unseen ones. If order does not matter, sort and drop adjacent duplicates: slices.Sort(s); s = slices.Compact(s).

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED