ProgrammingBackend Developer

What are the characteristics of working with data copying related to map and slice in Go, and how to avoid unexpected side effects when cloning, modifying, passing, and returning these structures from functions?

Pass interviews with Hintsage AI assistant

Answer.

The map and slice structures in Go have important copying characteristics and memory management semantics that often lead to unexpected behavior for inexperienced developers.

Background

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.

The Problem

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.

The Solution

  • When copying a slice, the new slice points to the same memory area if obtained via slicing (b := a[:]). To perform a complete copy of the elements, use the built-in copy() function.
  • Copying a map creates a shallow copy of the pointer. For a deep clone, one must loop through and copy each key-value pair.
  • Passing a slice or map to a function occurs by value, but the descriptor structure pointing to the same data is passed.

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 content
  • A complete clone requires manually (or via copy for slice) copying all data
  • Passing to or returning from a function does not copy contents — both participants can modify shared data

Trick Questions.

What 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.

Typical Mistakes and Anti-Patterns

  • Assigning maps or slices to each other, expecting independence, and unexpectedly getting side effects
  • Leaving "dangling" references to slices and then modifying the source, breaking invariants
  • Using the copy() function incorrectly, applying it to maps

Real-life Example

Negative Case

A developer copies a slice or map by assignment and modifies the copy for protection against side effects:

Pros:

  • Time saving on writing code
  • Fewer temporary variables

Cons:

  • Unexpected changes in other parts of the program
  • Hard-to-find bugs due to "invisible" sharing

Positive Case

Before modifying necessary data, copy() is used for slices and a loop for maps:

Pros:

  • Neat separation of data, independence of changes
  • Easy debugging and predictable behavior

Cons:

  • Requires more code
  • Additional allocations and memory copying