A Go map variable refers to mutable map state. Assigning that variable to another variable does not create an independent map: writes through either name affect the same map. When code needs a separate top-level map with the same entries, maps.Clone makes that boundary explicit.
The function is deliberately narrow. It copies map entries using ordinary assignment. The resulting map can be changed independently at the key-value entry level, but reference-like data stored inside keys or values can still share underlying state.
Map assignment and map cloning have different mutation boundaries
Consider a map whose values are integers:
package main
import (
"fmt"
"maps"
)
func main() {
original := map[string]int{"port": 8080}
alias := original
alias["port"] = 9090
fmt.Println(original["port"]) // 9090
copy := maps.Clone(original)
copy["port"] = 7070
fmt.Println(original["port"]) // 9090
fmt.Println(copy["port"]) // 7070
}The assignment to alias copies the map value, not the underlying map data. Both variables still refer to the same map. maps.Clone instead returns another map populated with the same entries, so replacing or deleting an entry in the clone does not replace or delete that entry in the source.
This distinction is useful when a function wants to retain a configuration snapshot at the map-entry level, derive a modified set of options, or return map data without exposing the original map container to direct insertion and deletion.
The clone is shallow
maps.Clone does not recursively duplicate values. If a map value contains a slice, map, pointer, function, channel, or another value that refers to mutable state, ordinary assignment preserves that reference relationship.
package main
import (
"fmt"
"maps"
)
func main() {
original := map[string][]int{
"ports": {8080, 8081},
}
copy := maps.Clone(original)
copy["ports"][0] = 9090
fmt.Println(original["ports"][0]) // 9090
}The outer maps are distinct, but the slice stored under "ports" in each map still refers to the same backing array. Replacing the whole slice entry in the clone would be isolated from the source; changing an element through the shared slice is not.
That behavior follows the function contract: keys and values are copied by assignment. A deep-copy requirement therefore needs type-specific logic that duplicates each mutable nested component with the intended ownership semantics.
Nil maps remain nil
Nilness is observable in Go even though reading from a nil map behaves much like reading from an empty map. A nil map compares equal to nil, cannot accept assignments, and can be encoded differently from an allocated empty map by some serializers.
maps.Clone preserves this distinction:
var source map[string]int
copy := maps.Clone(source)
fmt.Println(source == nil) // true
fmt.Println(copy == nil) // trueThis differs from a manual copy pattern that always allocates a destination with make. Such code turns a nil source into a non-nil empty destination unless it handles nil explicitly.
Preserving nilness can matter when nil represents an unset state rather than an explicitly supplied empty collection.
Named map types keep their type
The type parameter for maps.Clone accepts types whose underlying type is a map and returns the same map type. A named map type therefore does not collapse to an unnamed map[K]V.
type Headers map[string]string
original := Headers{
"Accept": "application/json",
}
copy := maps.Clone(original)
copy["Accept"] = "text/plain"Here copy has type Headers. That makes the function fit APIs that attach meaning or methods to a named map type without requiring a conversion after cloning.
Cloning does not add concurrency safety
Creating a clone is not a synchronization operation. Reading a built-in map while another goroutine writes to that same map remains unsafe unless access is coordinated.
A safe snapshot pattern needs synchronization around the source while its entries are copied. The lock or other ownership mechanism establishes a stable read period; maps.Clone only performs the copy.
After a clone has been created under suitable synchronization, handing the clone to code with exclusive ownership can reduce later coordination because top-level map mutations no longer target the original map. Shared nested values still require their own ownership or synchronization rules.
Copy and Clone express different operations
The maps package also provides maps.Copy, which writes entries from a source map into an existing destination map. Existing destination entries remain unless a source entry with the same key overwrites them.
maps.Clone has a different shape: it creates the destination and starts it with exactly the source entries. It also preserves a nil source as nil.
That difference is more than syntax. maps.Copy fits merging or populating an existing map. maps.Clone fits establishing a new top-level map identity while retaining the source’s entry set and map type.
The useful boundary is therefore precise: maps.Clone separates the map container, not the complete object graph reachable from its entries. Code that relies on the clone for isolation should decide whether top-level entry independence is sufficient or whether nested mutable values also need independent storage.