Creating and using a map
A map type is written map[KeyType]ValueType. Create one with a literal or with make, then read, write and delete by key.
Output:
31
3
map[bob:26 cy:40]
2 0
Two conveniences show up here. Reading a missing key returns the zero value of the value type (counts['z'] is 0), which makes counting with m[k]++ work with no setup. And fmt prints maps with sorted keys, which is handy for debugging but says nothing about iteration order.
make(map[K]V, n) takes an optional size hint. It preallocates room for about n entries; unlike slices, a map has no capacity you can read back.
Checking if a key exists: comma-ok
Because a missing key reads as the zero value, m[k] == 0 cannot tell "absent" from "stored as 0". Use the two-value form:
The if v, ok := m[k]; ok { ... } shape keeps v and ok scoped to the if. It is one of the most common lines in Go code.
Deleting entries
delete(m, key) removes the entry. Deleting a key that is not present is a no-op, and so is deleting from a nil map. To empty a whole map, Go 1.21 added clear(m), which keeps the allocated map so it can be reused.
Deleting entries during a range over the same map is allowed and safe. An entry deleted before the loop reaches it will not be produced.
Iterating: the order is random
for k, v := range m visits every entry once, in an unspecified order. The runtime randomizes the starting point on purpose, so two loops over the same map in the same program often disagree. Run this a few times:
Any code whose output depends on map order is a bug waiting for a different run. Tests that compare printed map iteration are the classic example.
Sorted keys
To visit a map in key order, get the keys, sort them, and index the map. Go 1.23 made this one line with iterators from the maps and slices packages:
On Go 1.22 and earlier, maps.Keys did not exist in the standard library. The equivalent is a loop:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
Other helpers in maps: maps.Values, maps.Clone (a shallow copy), maps.Equal, maps.Copy(dst, src) and maps.DeleteFunc.
Valid key types
Keys must be comparable with ==: numbers, strings, booleans, pointers, channels, arrays of comparable types, structs whose fields are all comparable, and interface values. Slices, maps and functions cannot be keys.
map[[]int]bool{} // compile error: invalid map key type []int
map[[2]int]bool{} // fine: arrays are comparable
map[struct{ X, Y int }]string{} // fine: a struct key for a grid position
A struct key is the idiomatic way to key by several values at once, instead of concatenating strings.
Interface keys compile even when the dynamic type is not comparable, and then panic at run time: storing a []int under a map[any]int fails with runtime error: hash of unhashable type []int.
Floating-point keys work but NaN is not equal to itself, so a NaN key can be inserted repeatedly and never read back. Avoid float keys.
Map of structs
A map can hold structs, but you cannot assign to a field of a struct stored in a map, because map values are not addressable.
Choose values when entries are small and replaced as a whole. Choose pointers when you update fields often or share the same record from several places. With pointers, a missing key returns nil, so ptrs["nope"].Score panics.
Maps of slices work the same way for appends: groups[k] = append(groups[k], v) needs no initialization, because a missing key gives a nil slice and append handles nil.
Maps are reference-like
A map value refers to shared data. Assigning a map or passing it to a function does not copy the entries: both variables see the same map.
That is why a function can fill a map without returning it, unlike a slice it appends to.
The nil map panic
The zero value of a map is nil. A nil map reads like an empty map, but writing to it panics.
Output:
0 0
recovered: assignment to entry in nil map
The struct case is the one that bites in practice. Initialize map fields in a constructor (func NewCache() *Cache { return &Cache{data: map[string]string{}} }) or lazily before the first write.
Concurrent access
Maps are not safe for concurrent use. If one goroutine writes while another reads or writes, the runtime may stop the program with fatal error: concurrent map writes (or concurrent map read and map write). This is a fatal error, not a panic, so recover cannot catch it.
Guard the map with a mutex:
This always prints 50 50. Use sync.RWMutex when reads far outnumber writes. sync.Map exists for two narrow cases (keys written once and read many times, or goroutines working on disjoint keys); for everything else a mutex and a plain map is simpler and usually faster. More on this in mutex.
Quick reference
| Operation | Code |
|---|---|
| Create | m := map[string]int{} or make(map[string]int) |
| Insert or update | m[k] = v |
| Read (zero if missing) | v := m[k] |
| Check presence | v, ok := m[k] |
| Delete | delete(m, k) |
| Remove all | clear(m) (Go 1.21) |
| Size | len(m) |
| Sorted keys | slices.Sorted(maps.Keys(m)) (Go 1.23) |
| Copy | maps.Clone(m) |
| Compare | maps.Equal(a, b) |
A map with struct{} values is also Go's set type; see sets.
Common mistakes
- Writing to a nil map. Always
makeit, including map fields in structs. - Relying on iteration order. Sort the keys.
- Using
m[k] != 0as a presence test. Use comma-ok. - Modifying a struct field through
m[k].Field. Copy out and write back, or store pointers. - Sharing a map between goroutines without a lock. The crash cannot be recovered.
Frequently Asked Questions
How do you check if a key exists in a Go map?
Use the two-value form of the lookup: v, ok := m[key]. ok is true when the key is present and false when it is not, in which case v is the zero value. Reading m[key] alone cannot tell a missing key from a key stored with the zero value.
Why is Go map iteration order random?
The language does not define an order, and the runtime deliberately starts each range at a random position so programs cannot come to depend on one. To iterate in key order, collect and sort the keys: for _, k := range slices.Sorted(maps.Keys(m)) (Go 1.23).
How do I get all keys of a map in Go?
Since Go 1.23, maps.Keys(m) returns an iterator; turn it into a slice with slices.Collect(maps.Keys(m)), or a sorted slice with slices.Sorted(maps.Keys(m)). Before 1.23, loop with for k := range m and append each key to a slice.
Why does writing to a map panic with "assignment to entry in nil map"?
The map variable was declared but never created: var m map[string]int is nil. Reading a nil map returns zero values, but writing panics. Create it first with m = make(map[string]int) or a literal m := map[string]int{}. A map field inside a struct needs the same initialization.
Are Go maps safe for concurrent use?
No. Concurrent writes, or a write concurrent with reads, can crash the program with fatal error: concurrent map writes, which recover cannot catch. Protect the map with a sync.Mutex or sync.RWMutex, or use sync.Map for the specific cases it is designed for.