Mutability and Data Sharing

Overview

Understanding when data is copied vs shared is crucial for avoiding bugs and writing efficient Go code.

Value vs Reference Semantics

Value Types (Copied)

// Integers, floats, bools, strings, arrays, structs
a := 1
b := a
b = 2
fmt.Println(a)  // 1 (unchanged)

arr := [3]int{1, 2, 3}
arr2 := arr
arr2[0] = 100
fmt.Println(arr[0])  // 1 (unchanged)

Reference Types (Shared)

// Slices, maps, channels, pointers
s := []int{1, 2, 3}
s2 := s
s2[0] = 100
fmt.Println(s[0])  // 100 (changed!)

m := map[string]int{"a": 1}
m2 := m
m2["a"] = 100
fmt.Println(m["a"])  // 100 (changed!)

Controlling Mutability

Immutable by Design

// Return new instead of modifying
func addItem(items []string, item string) []string {
    return append(items, item)  // May return new slice
}

Defensive Copy

func process(s []int) {
    local := make([]int, len(s))
    copy(local, s)  // Safe to modify local
}

Deep Copy

type User struct {
    Name    string
    Friends []string
}

func (u User) Clone() User {
    friends := make([]string, len(u.Friends))
    copy(friends, u.Friends)
    return User{Name: u.Name, Friends: friends}
}

Function Parameters

// Value: caller's data safe
func process(u User) {
    u.Name = "modified"  // Doesn't affect caller
}

// Pointer: can modify caller's data
func process(u *User) {
    u.Name = "modified"  // Affects caller
}

Struct Field Mutability

type Counter struct {
    count int
}

// Value receiver: cannot modify
func (c Counter) Increment() {
    c.count++  // Modifies copy, original unchanged
}

// Pointer receiver: modifies original
func (c *Counter) Increment() {
    c.count++  // Original modified
}

Slice Gotchas

// Append may or may not share backing array
s := []int{1, 2, 3}
s2 := s[:2]          // Shares backing array
s2 = append(s2, 4)   // Might overwrite s[2]!

// Safer: force new allocation
s2 := append([]int(nil), s[:2]...)

Summary

Type Behavior Copy When…
int, bool, string Value Always copied
struct Value Always copied
array Value Always copied
slice Reference Use copy()
map Reference Rebuild manually
pointer Reference -