skip to content

In Go's `maps` package, when do you use `maps.Insert` instead of `maps.Collect`?

level: middleimportance: nice to knowfreq 26%

answer

  1. one allocates, one is handed a destination
  2. check the return type of each
  3. think append versus AppendSeq
  4. merging two sequences needs the mutating one
  5. a destination you did not allocate can panic

basics

~20 s

maps.Collect allocates and returns a brand new map from a two-value sequence. maps.Insert writes the pairs into a map you already have and returns nothing. Use Insert to merge several sequences into one map, or to fill a map you pre-sized.

solid answer

~40 s

`maps.Collect(seq iter.Seq2[K, V]) map[K]V` builds a fresh map and returns it. `maps.Insert(m, seq iter.Seq2[K, V])` takes a map you already made and writes the pairs into it, returning nothing. So `Collect` is for "give me a map of this sequence" and `Insert` is for "add these pairs to that map". `Insert` is what you want when you are merging two or more sequences into one map, when the destination already holds entries you must keep, or when you pre-sized it with `make(map[K]V, n)` to avoid rehashing. Both write with plain map assignment, so a repeated key overwrites: the last pair yielded for a key wins. And because `Insert` writes into the map you pass, passing a nil map together with a non-empty sequence panics with the usual assignment to entry in nil map.

code

go · 8 lines
go
// Fresh map from one sequence:
m := maps.Collect(maps.All(base))

// Pre-sized destination fed from two sequences:
merged := make(map[string]int, len(base)+len(overrides))
maps.Insert(merged, maps.All(base))
maps.Insert(merged, maps.All(overrides)) // later pairs overwrite earlier ones
_ = m

go deeper

for a junior

Recall the shape difference: maps.Collect returns a new map, maps.Insert fills one you already made and returns nothing. Do not try to assign the result of Insert.

for a middle

Explain the concrete reasons to choose Insert: merging several sequences, a pre-sized map made with make, a destination that already holds entries, or a named map type. Know that repeated keys overwrite.

for a senior

Show that you catch the nil-destination panic in review, and note that it only fires when the sequence is non-empty, so an empty-input test will not find it.

for a principal

Frame it as an allocation-ownership question: helpers that take a destination let callers control sizing and reuse, which is the same reason your own APIs should consider accepting a destination rather than always returning a fresh container.

## The two collectors on the map side Go 1.23 gave the `maps` package two ways to turn an `iter.Seq2[K, V]` — a sequence of key/value pairs — back into a map: ```go func Collect[K comparable, V any](seq iter.Seq2[K, V]) map[K]V func Insert[Map ~map[K]V, K comparable, V any](m Map, seq iter.Seq2[K, V]) ``` The difference is ownership of the destination. `Collect` creates the map itself and hands it back. `Insert` is handed a map and mutates it in place; it has **no return value**, which is the first thing to remember, because writing `m = maps.Insert(m, seq)` will not compile. Mentally, `Collect` is to `Insert` what `slices.Collect` is to `slices.AppendSeq`: one allocates a fresh container, the other adds to a container you supply. ## When Insert is the right call **Merging.** A single `Collect` consumes exactly one sequence. To fold several sequences into one map you make the map once and `Insert` repeatedly: ```go merged := make(map[string]int, len(base)+len(overrides)) maps.Insert(merged, maps.All(base)) maps.Insert(merged, maps.All(overrides)) // overrides win on collisions ``` That also makes the precedence explicit and readable: later inserts overwrite earlier ones. **Pre-sizing.** `make(map[K]V, n)` reserves space for roughly `n` entries so the map does not rehash repeatedly as it fills. `Collect` cannot do that for you — a sequence has no length, so the helper has nothing to size from. If you know the count up front and it is large, making the map yourself and calling `Insert` is measurably cheaper. **Adding to something that already exists.** A cache, a registry, a read model that is being topped up — anywhere the destination map has entries you must not throw away. **A named map type.** `Insert` is constrained as `Map ~map[K]V`, so it accepts `type Counts map[string]int` and writes into it as that type. `Collect` returns the plain `map[K]V`, which you would then have to convert. ## The semantics that get asked about **Duplicate keys.** Neither helper deduplicates or errors. Both do the equivalent of `m[k] = v` for every pair, so if a sequence yields the same key twice, the map holds the value from the *last* pair yielded. If you need first-wins, check with `if _, ok := m[k]; !ok` in a hand-written loop instead. **A nil map.** Reading a nil map is fine, but writing to one panics. `maps.Insert` writes, so `var m map[string]int; maps.Insert(m, seq)` panics as soon as the sequence yields its first pair. It does not panic on an empty sequence, because no assignment ever happens — which makes this a lurking bug that only fires with real data. `maps.Collect` is immune: it makes the map itself. **Order.** A sequence has an order; a map does not. Collecting or inserting throws that order away, which is exactly why `maps.Collect(slices.All(s))` gives you a `map[int]E` keyed by the original positions — the index is preserved as data, not as layout. **Cost.** Both are O(n) in the number of pairs and both fully drain the sequence, so neither is lazy. If the sequence is unbounded, both will keep going until memory runs out. ## Choosing between them in review Reach for `Collect` when the map is a fresh result of the expression and nothing else will be added to it — it reads cleanly as one expression and there is no destination to get wrong. Reach for `Insert` the moment there are two sources, an existing destination, a known size, or a named map type. If you see `merged := maps.Collect(a)` followed by a hand-written loop copying `b` into it, that loop is an `Insert` call.

  • What happens if the sequence yields the same key twice?
    The last pair wins. Both helpers perform a plain `m[k] = v` per pair, with no deduplication and no error, so a later pair silently overwrites an earlier one. If you want first-wins semantics you have to write the loop yourself and guard with a comma-ok lookup before assigning.
  • What does maps.Insert return, and why does that catch people out?
    Nothing — it mutates the map argument in place. People expect the `append` shape, where you must reassign the result, and write `m = maps.Insert(m, seq)`, which fails to compile. Maps are reference-like: the header you pass already points at the same buckets, so no reassignment is needed.
  • Why can maps.Collect never panic on a nil destination the way maps.Insert can?
    `maps.Collect` allocates the map itself before writing anything, so there is no caller-supplied destination to be nil. `maps.Insert` writes into whatever map it is given, and assigning to an entry of a nil map panics. The trap is that an empty sequence never triggers it, so the bug can survive tests and fire in production.

saying these in an interview costs you the question

  • Writes m = maps.Insert(m, seq) expecting a returned map
  • Thinks maps.Insert skips keys that already exist
  • Believes maps.Collect pre-sizes the map from the sequence
  • Assumes writing to a nil map allocates it
  • Says the helpers preserve the sequence's order in the map