The map and slice structures in Go have important copying characteristics and memory management semantics that often lead to unexpected behavior for inexperienced developers.
Although Go is considered a strict language with static typing and lacks pointers by default, both map and slice have a special internal model: both types are reference structures. This imposes limitations and creates many nuances when copying and passing these objects.
Copying a map and slice does not lead to deep copying of the contents, but instead creates a new reference to the same object, resulting in unexpected side effects when modifying data, incorrect return values from functions, and modifications. Additionally, returning a map or slice as a function result can trigger additional allocations or leaks.
b := a[:]). To perform a complete copy of the elements, use the built-in copy() function.Example of correct copying:
// Copying a slice a := []int{1, 2, 3} b := make([]int, len(a)) copy(b, a) // b is now independent from a // Copying a map src := map[string]int{"x": 1} dst := make(map[string]int) for k, v := range src { dst[k] = v }
Key characteristics:
slice and map are reference types, copied by descriptor, not by contentWhat happens if you simply assign one map/slice to another and then modify one of them?
Both map and slice will point to the same data in memory: changes will affect both objects.
Why is it often said that returning a slice or map from a function is "memory efficient"?
Because a copy of the descriptor is returned, not the entire content; the data in the heap lives as long as there are references to it.
Can copy() be used for "deep" copying a map?
No, copy() only works with slices and arrays; a loop is always needed for maps.
A developer copies a slice or map by assignment and modifies the copy for protection against side effects:
Pros:
Cons:
Before modifying necessary data, copy() is used for slices and a loop for maps:
Pros:
Cons: