ProgrammingGo Developer

How does map[string]struct{} work as a set in Go and what are the features of such an application?

Pass interviews with Hintsage AI assistant

Answer.

Background:

Go does not have a built-in Set structure by default, but the need to work with unique elements often arises. The optimal structure is map[string]struct{}, where the key is the element, and the empty struct serves as a "presence marker". This is a common pattern for fast membership testing.

Problem:

The lack of a built-in Set leads newcomers to find it challenging to implement unique collections correctly. It's also important to understand why struct{} is more efficient than bool or int as a value.

Solution:

To implement a Set in Go, map[string]struct{} is used. The empty struct struct{} requires no memory (zero-sized), while the map provides fast access. Example:

set := make(map[string]struct{}) set["foo"] = struct{}{} if _, ok := set["foo"]; ok { fmt.Println("Present") } delete(set, "foo")

Key Features:

  • struct{} occupies 0 bytes — an economical implementation
  • map provides O(1) access by key
  • No duplicate elements, the Set semantics are easy to implement

Tricky Questions.

Why can't you use slice/array as a value?

Slice/array for a set does not provide constant time for element search — it would require iterating through all values, which is slow.

What is the difference between map[string]struct{} and map[string]bool?

map[string]bool takes up more memory: it stores a bool for each key, while struct{} is an empty type that allocates nothing.

set := map[string]bool{"foo": true}

Can you use int instead of struct{}?

You can, but int always occupies memory. struct{} is universal: if you only need a role as a "marker" (presence), it's better.

set := map[string]int{"foo": 1} // but stores (key -> number)

Common Mistakes and Anti-Patterns

  • Using bool or int for values unnecessarily
  • Using slice to check for an element's presence (slows down checks)
  • Forgetting to delete elements using delete

Real-life Example

Negative Case

Due to lack of knowledge, map[string]bool was assigned for a set of unique IP addresses. As a result, memory consumption doubled compared to struct{} with millions of addresses.

Pros:

  • Semantically clear (true == exists)

Cons:

  • Lower performance
  • Higher memory usage

Positive Case

In the project, map[string]struct{} was used to store unique emails. The load decreased, it worked faster, and almost no memory was spent on values.

Pros:

  • Minimal overhead
  • Performance with a large number of elements

Cons:

  • Less obvious for beginners, requires comments in the code