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:
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)
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:
Cons:
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:
Cons: