A normal range loop is often the clearest way to walk through a Go map. The situation changes when another API expects an iterator rather than a map. Starting in Go 1.23, maps.All provides that bridge by exposing a map’s key-value pairs as an iter.Seq2.

maps.All doesn’t copy the map into a slice or build a second map. It returns an iterator that can feed a range loop or another iterator-aware function. The main constraint is familiar from map iteration: pair order is unspecified.

Iterate over key-value pairs with maps.All

For direct iteration, maps.All can appear on the right side of range:

package main

import (
    "fmt"
    "maps"
)

func main() {
    scores := map[string]int{
        "ana":  91,
        "dion": 84,
        "mira": 96,
    }

    for name, score := range maps.All(scores) {
        fmt.Printf("%s: %d\n", name, score)
    }
}

Each iteration produces two values: a key and its associated value. The type returned by maps.All(scores) is an iter.Seq2[string, int].

For this example, an ordinary map loop is shorter:

for name, score := range scores {
    fmt.Printf("%s: %d\n", name, score)
}

That isn’t a reason to replace existing map loops. maps.All earns its place when the iterator itself is useful, especially at an API boundary.

Pass map pairs to iterator-aware code

An iter.Seq2 is a function-based sequence of pairs. Code that accepts this type doesn’t need to know whether its input came from a map, a slice index-value sequence, or a custom producer.

For example, a helper can print any string-int pair sequence:

package main

import (
    "fmt"
    "iter"
    "maps"
)

func printPairs(seq iter.Seq2[string, int]) {
    for key, value := range seq {
        fmt.Printf("%s=%d\n", key, value)
    }
}

func main() {
    limits := map[string]int{
        "api":   20,
        "batch": 5,
    }

    printPairs(maps.All(limits))
}

The helper depends on the sequence shape rather than the concrete map type. That can be useful when several producers need to feed the same processing code.

There is still a trade-off. If a function only ever needs a map, accepting the map directly is simpler and preserves operations such as key lookup. Converting every map parameter into an iterator adds abstraction without adding capability.

Compose maps.All with maps.Insert

The standard library’s maps.Insert accepts an iter.Seq2, so maps.All can connect one map to it directly:

defaults := map[string]int{
    "workers": 4,
    "retries": 2,
}

overrides := map[string]int{
    "workers": 8,
}

maps.Insert(defaults, maps.All(overrides))

fmt.Println(defaults["workers"]) // 8
fmt.Println(defaults["retries"]) // 2

Here, maps.All(overrides) produces the pairs and maps.Insert writes them into defaults. Existing keys are overwritten when the iterator supplies the same key.

If both inputs are already maps, maps.Copy(defaults, overrides) expresses this particular operation more directly. The All and Insert combination is more compelling when the producer is already represented as an iterator or when code is designed around iter.Seq2.

Do not depend on pair order

maps.All does not define an iteration order, and separate calls are not guaranteed to produce pairs in the same order. Code that needs deterministic output should impose an order explicitly.

One practical approach is to sort the keys and then look up each value:

package main

import (
    "fmt"
    "maps"
    "slices"
)

func main() {
    scores := map[string]int{
        "mira": 96,
        "ana":  91,
        "dion": 84,
    }

    keys := slices.Sorted(maps.Keys(scores))

    for _, key := range keys {
        fmt.Printf("%s: %d\n", key, scores[key])
    }
}

This is a better fit for stable logs, snapshots, generated text, or tests that compare ordered output. maps.All is suitable when pair order carries no meaning.

Sorting the values alone doesn’t solve ordering by key because it discards the association between each key and value. Choose the ordering rule first, then materialize or sort the data needed to enforce it.

A nil map produces no pairs

A nil map is valid input to maps.All. Since the map contains no entries, ranging over its iterator performs zero iterations:

var counts map[string]int

for key, value := range maps.All(counts) {
    fmt.Println(key, value)
}

fmt.Println("done")

The loop body doesn’t run, and the program prints done.

This matches ordinary ranging over a nil map. You don’t need a special nil check merely to iterate. A nil check can still matter when nil has domain meaning distinct from an initialized empty map.

Keep the iterator boundary intentional

Use maps.All when a map needs to participate in code that consumes iter.Seq2 values. It gives that code a key-value sequence without forcing an intermediate collection.

For a plain loop, range over the map directly. For map-to-map copying, maps.Copy is clearer. When order matters, sort according to the rule your output requires. Keeping those distinctions visible makes maps.All a focused adapter rather than an extra layer around every map operation.