Using Maps in Go: Declarations, Lookups, and Iteration
Use Go maps correctly: initialize with make, detect missing keys, use len and delete, and iterate without depending on map order.

Go maps require three habits: initialize a map before writing to it, use the two-value lookup when key existence matters, and never make program logic depend on iteration order. A map declared with var starts as nil; reads behave as they would on an empty map, but a write causes a runtime panic. Use make before populating it.
Declaring and populating a map
A map type has the form map[KeyType]ValueType. The key type must be comparable, while the value can be any type. Strings are therefore suitable keys for a route counter:
var counts map[string]int
The default value of this variable is nil. Reading it is valid and returns the value type's zero value, but writing to it panics:
package main
import "fmt"
func main() {
var counts map[string]int
// A read from a nil map behaves like a read from an empty map.
fmt.Println(counts["route"]) // 0
// If uncommented and executed, this write causes a runtime panic.
// counts["route"] = 1
}
The Go maps article states the distinction directly: a nil map behaves like an empty map for reads, but attempts to write to it panic.
Initialize the map with make before adding entries:
counts := make(map[string]int)
counts["route"] = 1
counts["health"] = 3
make allocates and initializes the underlying hash map data structure. Assigning another value to an existing key replaces its old value:
counts["route"] = 2
Lookups and key existence
A one-value lookup returns the stored value when the key exists. When it does not exist, it returns the zero value of the map's value type:
count := counts["missing"] // 0
That creates an ambiguity. A result of 0 could mean that the key is absent, or it could be a value deliberately stored under that key.
Use the two-value form when the difference matters:
counts := make(map[string]int)
counts["route"] = 0
i, ok := counts["route"]
fmt.Println(i, ok) // 0 true
j, found := counts["missing"]
fmt.Println(j, found) // 0 false
In i, ok := counts["route"], i receives the stored value or the value type's zero value. The Boolean ok reports whether the key exists. It is the existence check—not the numeric value—that distinguishes these cases.
Counting and deleting entries
The built-in len function reports the number of key/value pairs in a map:
counts := make(map[string]int)
counts["route"] = 2
counts["health"] = 3
fmt.Println(len(counts)) // 2
Use the built-in delete function to remove an entry:
delete(counts, "health")
fmt.Println(len(counts)) // 1
The built-in len and delete operations are documented in the Go by Example map material.
Iterating without assuming an order
A range loop visits a map's key/value pairs:
for route, count := range counts {
fmt.Println(route, count)
}
Maps are unordered, so code should not attach meaning to the sequence produced by this loop. For example, do not assume that "health" will appear before "route", or use the first visited entry as though it were the smallest key.
The standard library maps package also provides iterators. maps.All returns an iterator over key/value pairs:
package main
import (
"fmt"
"maps"
)
func main() {
counts := map[string]int{
"route": 2,
"health": 3,
}
for route, count := range maps.All(counts) {
fmt.Println(route, count)
}
}
Use maps.Keys when only the keys are needed:
for route := range maps.Keys(counts) {
fmt.Println(route)
}
For both maps.All and maps.Keys, the documented iteration order is unspecified and is not guaranteed to remain the same from one call to the next. These iterators are convenient ways to visit entries or keys; they do not add an ordering guarantee.
A complete lookup pattern
The following example combines initialization, updates, and an explicit success check:
package main
import "fmt"
func main() {
counts := make(map[string]int)
counts["route"] = 4
counts["health"] = 0
if count, ok := counts["health"]; ok {
fmt.Printf("health exists with count %d\n", count)
} else {
fmt.Println("health is missing")
}
delete(counts, "route")
fmt.Printf("entries remaining: %d\n", len(counts))
}
The important detail is that "health" succeeds even though its stored value is zero. Checking count != 0 would answer a different question and would incorrectly classify this entry as missing.
For routine Go map use, the resulting rules are compact: call make before writing to a map that was declared with var, use the comma-ok lookup whenever existence matters, and treat every iteration sequence as unordered.





Comments
No comments yet.