Question

Go Map Initialization: make vs Map Literal

Question

In Go, maps are reference types. What is the difference between initializing an empty map with make and using a map literal?

copyM := make(map[string]int32)
copyM := map[string]int32{}

Which form is generally preferable, and when should each be used?

Short Answer

You will learn how Go map initialization works, why both examples create writable empty maps, and when make, a map literal, or a nil map is the most appropriate choice.

Concept

A Go map is a reference-like data structure: a map variable refers to an underlying hash-table data structure managed by Go.

Both of these statements create an empty, non-nil, writable map:

copyM := make(map[string]int32)
copyM := map[string]int32{}

After either statement, you can safely assign entries:

copyM["books"] = 3

For an empty map with no special requirements, the two forms behave the same in normal use. The main differences are intent and features:

  • Use make(map[K]V) when you want to create an empty map, especially when supplying a capacity hint.
  • Use map[K]V{} when you want literal syntax, especially when providing initial key-value entries.

A capacity hint can help when you expect to add many entries:

scores := make(map[string]int, 1_000)

The number is a hint, not a map length. len(scores) is still 0 immediately after creation.

Mental Model

Think of a map as a filing cabinet.

  • A nil map is a label for a cabinet that has not been delivered yet. You can ask whether a file exists, but you cannot put a file into it.
  • make(map[string]int) is ordering and receiving an empty filing cabinet.
  • map[string]int{} is also receiving an empty filing cabinet, but using literal notation.
  • map[string]int{"Ada": 10} is receiving a cabinet that already contains labeled files.

The two empty-cabinet forms are equivalent for adding and reading entries. Choose the notation that makes your code's purpose clearest.

Take Quiz

Syntax and Examples

Create an empty writable map with make:

counts := make(map[string]int)
counts["apple"] = 2

Create an empty writable map with a literal:

counts := map[string]int{}
counts["apple"] = 2

Create a map with initial entries using a literal:

prices := map[string]float64{
    "coffee": 3.50,
    "tea":    2.75,
}

Create a map with a capacity hint using make:

users := make(map[string]bool, 500)

The 500 does not insert 500 elements. It tells Go that the map may grow to around that size, which can reduce internal resizing work when many entries will be added.

A declared map without initialization is nil:

var counts map[string]int

fmt.Println(counts == nil) // true
fmt.Println(counts["apple"]) // 0: reading is safe

// counts["apple"] = 2 // panic: assignment to entry in nil map

Step by Step Execution

Consider this program:

package main

import "fmt"

func main() {
    inventory := map[string]int{}
    inventory["pen"] = 4

    inventory["pen"]++

    fmt.Println(inventory)
    fmt.Println(len(inventory))
}

Execution flow:

  1. map[string]int{} creates an empty, non-nil map whose keys are string values and whose values are int values.
  2. inventory["pen"] = 4 adds the key "pen" with value 4.
  3. inventory["pen"]++ reads the current value (4), adds one, and stores 5 for the same key.
  4. Printing the map displays data equivalent to map[pen:5]. Map display order should not be relied on when several entries exist.
  5. len(inventory) returns 1, because the map has one key.

Replacing the first line with the following produces the same result:

inventory := make(map[string]int)

Real World Use Cases

  • Counting events: Store how many times each endpoint, product, or error code occurs.

    visits := map[string]int{}
    visits["/pricing"]++
    
  • Building a lookup table: Associate user IDs with user records loaded from a database or API.

    usersByID := make(map[string]User, len(users))
    
  • Removing duplicates: Use a map as a set, often with struct{} values.

    seen := make(map[string]struct{})
    seen["go"] = struct{}{}
    
  • Configuration defaults: Use a map literal when the default values are known in source code.

    limits := map[string]int{
        "free":  100,
        "pro":   10_000,
    }
    
  • JSON-like dynamic data: Build a map[string]any before sending data to an API or encoding JSON.

Take Quiz

Real Codebase Usage

In real Go projects, the form usually communicates intent.

Use a literal for fixed initial data

var statusText = map[int]string{
    200: "OK",
    404: "Not Found",
    500: "Internal Server Error",
}

This is clear because the map's contents are part of the definition.

Use make while collecting data dynamically

func groupByTeam(members []Member) map[string][]Member {
    groups := make(map[string][]Member)

    for _, member := range members {
        groups[member.Team] = append(groups[member.Team], member)
    }

    return groups
}

Supply a capacity hint for known input sizes

func indexByID(records []Record) map[string]Record {
    index := make(map[string]Record, len(records))

    for _, record := range records {
        index[record.ID] = record
    }

    return index
}

This is a common indexing pattern. The hint is an optimization, not a correctness requirement.

Preserve nil when it has meaning

Some APIs use nil to mean “not supplied” and an empty map to mean “supplied, but empty.” Do this only when that distinction is meaningful to callers, serialization, or business rules.

Common Mistakes

Writing to a nil map

This compiles, but it panics at runtime:

var totals map[string]int
totals["order"] = 1 // panic: assignment to entry in nil map

Initialize it first:

totals := make(map[string]int)
totals["order"] = 1

Assuming make creates entries

scores := make(map[string]int, 100)
fmt.Println(len(scores)) // 0, not 100

The second argument is only a capacity hint.

Confusing reading with writing on a nil map

Reading is safe and returns the value type's zero value:

var flags map[string]bool
fmt.Println(flags["enabled"]) // false

Writing is not safe unless the map is initialized.

Forgetting that a missing key returns a zero value

ages := map[string]int{}
fmt.Println(ages["unknown"]) // 0

Use the comma-ok form when you must distinguish a missing key from a stored zero value:

age, found := ages["unknown"]
fmt.Println(age, found) // 0 false

Relying on map iteration order

Do not assume this loop has a predictable order:

for key, value := range ages {
    fmt.Println(key, value)
}

If ordering matters, collect and sort the keys first.

Comparisons

FormResultCan write entries?Best use
var m map[string]intNil mapNoOptional map that may remain absent
make(map[string]int)Empty non-nil mapYesDynamically populated map
make(map[string]int, n)Empty non-nil map with capacity hintYesMany expected insertions
map[string]int{}Empty non-nil mapYesConcise empty literal or consistency with literals
map[string]int{"a": 1}Non-nil map with entriesYesKnown initial values

make is used for maps, slices, and channels because these types need runtime initialization. A composite literal such as map[string]int{} is useful when declaring the map structure and, optionally, its starting contents in one expression.

For the original two forms, neither is universally “nicer.” A common convention is:

  • prefer map[K]V{} when a literal is natural or initial entries are nearby;
  • prefer make(map[K]V) when the map will be filled programmatically;
  • prefer make(map[K]V, len(input)) when an appropriate capacity estimate is known.
Take Quiz

Cheat Sheet

// Nil map: reads work; writes panic
var m map[string]int

// Empty, writable maps
m1 := make(map[string]int)
m2 := map[string]int{}

// Empty, writable map with capacity hint
m3 := make(map[string]int, 100)

// Map with initial entries
m4 := map[string]int{
    "apples":  3,
    "oranges": 5,
}

// Insert or update
m1["apples"] = 3

// Read; missing keys produce the zero value
count := m1["missing"] // 0

// Check whether a key exists
count, ok := m1["apples"]

// Delete a key
delete(m1, "apples")

Rules:

  • make(map[K]V) and map[K]V{} both create writable empty maps.
  • make(map[K]V, n) uses n as a capacity hint, not the length.
  • Only map literals can include initial key-value entries directly.
  • A nil map can be read, ranged over, and passed to len, but cannot receive assignments.
  • Map iteration order is not guaranteed.

FAQ

Is make(map[string]int) the same as map[string]int{} in Go?

For creating an empty map that you can read from and write to, yes. Both produce an empty, non-nil map.

Which map initialization style should I use in Go?

Use a literal when declaring known initial entries. Use make when creating an empty map to fill dynamically, especially when you have a useful capacity estimate.

Does make(map[string]int, 10) create 10 map entries?

No. It creates an empty map with a capacity hint for approximately 10 entries. Its length is still zero.

Why does assignment to a nil map panic?

A nil map has no initialized underlying storage for new entries. Initialize it with make or a map literal before assigning keys.

Can I read from a nil map in Go?

Yes. Reading a missing key from a nil map returns the zero value for the map's value type. Writing still panics.

Can I compare two maps with == in Go?

Maps cannot be compared to each other. A map can only be compared with nil, such as m == nil.

Does an empty map equal a nil map?

They are different states. An empty map is initialized and writable; a nil map is uninitialized. Both have length zero.

Take Quiz

Mini Project

Description

Build a small word-frequency counter for a text string. It demonstrates how to initialize a map, add values dynamically, use a capacity hint, and check the result for a requested word.

Goal

Create a function that counts each word in a sentence and reports the count of a selected word.

Requirements

Initialize a map that stores words as keys and counts as integer values. Convert the input text to lowercase before counting. Count each word by updating its map entry. Print every word and its count. Use the comma-ok map lookup form to report whether a requested word was present.

Take Quiz

Keep learning

Related questions

View more