A map in Go is a key-value collection for looking up data by an identifier, like a dictionary where each key points to a specific value.
So far we’ve seen how to store data in a queue using arrays and slices. They work well if we know the order or the numeric index. To search by an ID, a name, or any other comparable key, we use maps.
In other languages, they are called dictionaries or hash maps. The concept is the same: a structure with no guaranteed iteration order that associates a unique key with a value.
What is a map in Go?
A map in Go is a reference to a hash table.
The syntax to define it is: map[KeyType]ValueType.
- Key type: can be any comparable type, such as booleans, numbers, strings, channels, pointers, and certain arrays, structs, and interfaces. It cannot be a slice, a map, or a function.
- Value type: can be any type, even another map.
Declaration and initialization
Here’s a trap that almost all beginners fall into.
If you declare a map with just var, you get a nil map. A nil map behaves curiously:
- You can read from it (it returns zero values).
- You cannot assign an entry: it causes a
panic.
package main
import "fmt"
func main() {
// ❌ DANGEROUS WAY
var colors map[string]string // It's nil
// colors["red"] = "#FF0000" // ERROR: panic: assignment to entry in nil map
// ✅ CORRECT WAY 1: make()
// Initializes the map and reserves memory
scores := make(map[string]int)
scores["Luis"] = 10
// ✅ CORRECT WAY 2: Literal
// Very useful for initial values
capitals := map[string]string{
"Spain": "Madrid",
"France": "Paris",
"Italy": "Rome", // Note the mandatory trailing comma!
}
fmt.Println(capitals)
}Use make(map[...]) or a literal if you are going to write to the map. A nil map is still useful as a read-only empty value.
Basic operations
Working with maps is very intuitive:
Insert or update
There is no difference. If the key doesn’t exist, it creates it. If it exists, it overwrites it.
m := make(map[string]int)
m["age"] = 30
m["age"] = 31 // UpdatesRead a value
value := m["age"]Delete a key
Go has a built-in function called delete.
delete(m, "age")Fun fact: If you try to delete a key that doesn’t exist, nothing happens (no error).
The zero value and the comma, ok pattern
What happens if you try to read a key that does not exist?
In other languages, you’d get an exception or null. In Go, it returns the Zero Value of the value type.
scores := map[string]int{"Anna": 10}
grade := scores["Pedro"] // Pedro doesn't exist
fmt.Println(grade) // Prints 0 (because it's the zero value of int)This is a problem. Does Pedro have a 0 because he failed, or does he have a 0 because he didn’t take the exam (he isn’t in the map)?
To distinguish these cases, we use the multiple assignment, known as the “Comma Ok” pattern:
value, ok := map[key]value: The stored value (or zero if not present).ok: A boolean (trueif the key exists,falseif not).
if grade, ok := scores["Pedro"]; ok {
fmt.Println("Pedro's grade is:", grade)
} else {
fmt.Println("Pedro is not on the list")
}This pattern is common in Go. Use it when you need to distinguish between an absent key and a stored zero value.
Iterating over maps
To traverse a map, we use the for loop with range, just like with slices, but we get key and value.
menu := map[string]float64{
"Coffee": 1.50,
"Toast": 2.00,
"Juice": 3.00,
}
for dish, price := range menu {
fmt.Printf("%s costs %.2f€\n", dish, price)
}Order is not guaranteed
This is critical. Maps in Go are not ordered.
Every time you run the for range loop above, the elements might come out in a different order. Simply do not depend on that order.
If you need to iterate in order (for example, alphabetically), you must:
- Extract the keys into a slice.
- Sort that slice.
- Iterate over the slice and look up the map.
Maps and shared data
As with slices, when you pass a map to a function, a small descriptor is copied, but both point to the same internal table. If the function modifies it, the original map changes.
func modifyMap(m map[string]int) {
m["new"] = 999
}
func main() {
myMap := make(map[string]int)
modifyMap(myMap)
fmt.Println(myMap) // Prints map[new:999]
}