Sometimes you already have a destination map and need to add another map’s entries without writing a loop. Go’s maps.Copy does exactly that: it copies every key-value pair from a source map into an existing destination map.
The operation is deliberately simple. Existing destination entries remain when their keys aren’t present in the source. When both maps contain the same key, the source value replaces the destination value. That makes maps.Copy useful for applying defaults, overrides, accumulated state, and other map-to-map merges where replacement is the intended conflict rule.
Copy map entries into an existing destination
Suppose an application starts with default settings and then applies a smaller set of overrides:
package main
import (
"fmt"
"maps"
)
func main() {
settings := map[string]int{
"workers": 4,
"retries": 2,
}
overrides := map[string]int{
"workers": 8,
}
maps.Copy(settings, overrides)
fmt.Println(settings["workers"]) // 8
fmt.Println(settings["retries"]) // 2
}maps.Copy(settings, overrides) mutates settings. The workers entry is replaced because that key exists in both maps. The retries entry stays untouched because the source has no value for that key.
The function returns no value. Its effect is the change to the destination map.
Source values replace matching destination values
The order of the arguments matters:
maps.Copy(dst, src)Think of the operation as assigning every source entry into the destination. Conceptually, it behaves like this loop:
for key, value := range src {
dst[key] = value
}This replacement rule is convenient for layered configuration:
config := map[string]string{
"region": "us-east",
"format": "json",
"timeout": "5s",
}
environment := map[string]string{
"region": "eu-west",
"timeout": "10s",
}
maps.Copy(config, environment)After the call, config keeps "format": "json" while the two matching entries take their values from environment.
If your merge needs a different conflict policy, such as preserving the old value or combining both values, maps.Copy isn’t enough by itself. A small explicit loop is usually clearer because it gives you control over each collision.
maps.Copy performs a shallow copy
Copying an entry doesn’t recursively duplicate data referenced by its value. Ordinary Go assignment rules still apply.
Consider a map whose values are slices:
source := map[string][]int{
"ports": {80, 443},
}
destination := map[string][]int{}
maps.Copy(destination, source)
destination["ports"][0] = 8080
fmt.Println(source["ports"][0]) // 8080The destination gets its own map entry, but both entries contain slice values that refer to the same backing array. Changing an element through one slice can therefore be visible through the other.
The same caveat applies to pointers, maps used as values, and structs containing reference-bearing fields. If the destination needs independent nested data, copy that nested data explicitly.
The destination map must be writable
A non-nil map can receive entries immediately:
destination := make(map[string]int)
maps.Copy(destination, source)A nil destination can’t accept assignments. Passing one to maps.Copy when the source contains entries leads to the same kind of runtime panic you’d get from assigning directly into a nil map.
A nil source is different. It has no entries to copy, so copying from it leaves the destination unchanged.
This distinction matters when maps come from optional configuration or zero-value struct fields. Initialize the destination before copying into it when it may be nil.
Choose maps.Copy when existing destination state should survive
maps.Copy is most useful when you already have a map whose unrelated entries should remain. It mutates that map and replaces only keys supplied by the source.
For a separate copy of one entire map, maps.Clone expresses the intent more directly:
duplicate := maps.Clone(source)For iterator-produced key-value pairs, maps.Insert can write those pairs into an existing map. If the source is already a map, maps.Copy avoids converting it into an iterator first.
These helpers overlap at the edges, but their intent differs: Clone creates another top-level map, Copy transfers entries between maps, and Insert consumes key-value pairs from an iterator.
Keep collision behavior visible at the call site
Use maps.Copy when replacing matching keys is part of the operation you want. A call such as maps.Copy(config, overrides) communicates that relationship with very little code.
Before using it, check two details: the destination must be ready for writes, and copied reference-bearing values remain shallow. If either condition needs custom handling, an explicit loop can make those rules easier to see and enforce.