Assigning one map variable to another does not copy entries; you need a second map and a loop (or serialization) to golang copy map data. A range loop always builds a new top-level map, but values that are reference types may still alias the original unless you copy them deliberately—a shallow versus deep distinction explained below. For map basics, see maps in Go.
Tested with Go 1.24 on Linux.
Shallow copy versus deep copy
A shallow copy is a new map whose values are the same references as the original (slices, nested maps, pointers). A deep copy duplicates those inner structures so mutating the copy does not change the original. Maps themselves are reference types; only the key-value table is new after a typical loop copy.
Copy with a range loop (often shallow)
Values that are independent (int, string, structs of primitives)
package main
import "fmt"
func main() {
orig := map[string]int{"a": 1, "b": 2}
dup := make(map[string]int, len(orig))
for k, v := range orig {
dup[k] = v
}
dup["a"] = 99
fmt.Println(orig["a"], dup["a"])
}You should see 1 then 99.
Shared inner slices (shallow)
package main
import "fmt"
func main() {
orig := map[string][]int{"nums": {1, 2, 3}}
dup := make(map[string][]int)
for k, v := range orig {
dup[k] = v
}
dup["nums"][0] = 99
fmt.Println(orig["nums"][0])
}You should see 99 because both maps point at the same backing array.
Duplicating slice values (deep enough for slices)
package main
import "fmt"
func main() {
orig := map[string][]int{"nums": {1, 2, 3}}
dup := make(map[string][]int)
for k, v := range orig {
cp := make([]int, len(v))
copy(cp, v)
dup[k] = cp
}
dup["nums"][0] = 99
fmt.Println(orig["nums"][0], dup["nums"][0])
}You should see 1 and 99.
Deep copy with encoding/json
Use JSON when the value shapes are JSON-serializable and you want a generic deep-ish copy; check errors and expect map[string]any for nested objects after Unmarshal.
package main
import (
"encoding/json"
"fmt"
)
func main() {
orig := map[string]any{"key": map[string]int{"one": 1}}
b, err := json.Marshal(orig)
if err != nil {
panic(err)
}
var dup map[string]any
if err := json.Unmarshal(b, &dup); err != nil {
panic(err)
}
inner := dup["key"].(map[string]any)
inner["one"] = 100
fmt.Println(orig["key"].(map[string]int)["one"])
fmt.Println(inner["one"])
}You should see 1 then 100. More on JSON workflows: JSON unmarshal in Go and map to JSON.
Reflection and third-party helpers
The reflect package can build generic deep copies but is subtle and usually slower than typed code (performance notes). Libraries such as github.com/jinzhu/copier are common for structs and maps in applications that already depend on them—add them in your own go.mod and run locally; they are not Playground-default snippets here.
Summary
To golang copy map data, decide whether shared inner state is acceptable. A range loop plus make duplicates the map header; duplicate slices with copy, nested maps with another loop or JSON, and pointers by allocating new values when you need isolation. JSON marshal and unmarshal is a practical deep copy path for JSON-compatible trees, with explicit error handling and type assertions at the edges.

