Slices and maps contain pointers to the underlying data so be wary of scenarios when they need to be copied.
Declaring Empty Slices
When declaring an empty slice, prefer
var t []string
over
t := []string{}
The former declares a nil slice value, while the latter is non-nil but zero-length. They are functionally equivalent—their len and cap are both zero—but the nil slice is the preferred style.
CopyingSlices and Maps
Keep in mind that users can modify a map or slice you received as an argument if you store a reference to it.
// BADfunc (d *Driver) SetTrips(trips []Trip) { d.trips = trips}trips :=...d1.SetTrips(trips)// Did you mean to modify d1.trips?trips[0] =...
// GOODfunc (d *Driver) SetTrips(trips []Trip) { d.trips =make([]Trip, len(trips))copy(d.trips, trips)}trips :=...d1.SetTrips(trips)// We can now modify trips[0] without affecting d1.trips.trips[0] =...
Returning Slices and Maps
Similarly, be wary of user modifications to maps or slices exposing internal state.
// BADtypeStatsstruct { mu sync.Mutex counters map[string]int}// Snapshot returns the current stats.func (s *Stats) Snapshot() map[string]int { s.mu.Lock()defer s.mu.Unlock()return s.counters}// snapshot is no longer protected by the mutex, so any// access to the snapshot is subject to data races.snapshot := stats.Snapshot()
// GOODtypeStatsstruct { mu sync.Mutex counters map[string]int}func (s *Stats) Snapshot() map[string]int { s.mu.Lock()defer s.mu.Unlock() result :=make(map[string]int, len(s.counters))for k, v :=range s.counters { result[k] = v }return result}// Snapshot is now a copy.snapshot := stats.Snapshot()
var result []interface{}// i and value are loop iteration values.for i, item :=range mySlice {// Bad result =append(result, &item) // NEVER create a pointer to any iteration variable.// Good result =append(result, &mySlice[i]) // Alternative, create a pointer using the index// Good (by copying) temp := item // Alternative, first copy the iteration variable. result =append(result, &temp) // Then create a pointer to the copy. }
Map
var result []interface{}// key and value are loop iteration values.for key, value :=range myMap {// Bad result =append(result, &value) // NEVER create a pointer to any iteration variable.// Good temp := value // Instead, first copy the iteration variable. result =append(result, &temp) // Then create a pointer to the copy. }
Don't create new variables with append
// Badnotnew := append(old, add) // notnew might not be a new independent slice, but point to the same array as old, so modification to old can affect notnew.
// Goodcopied :=append([]byte(nil), old...) // First do a proper copycopied =append(copied, add) // Then append to itself// Ok, but weird and easy to get wrongcopied := append(old[0:0:0], old...) // Another way to copy: `old[0:0:0]` is empty slice with same type as old. Note not including the third zero is BAD.
copied =append(copied, add) // Then append to itself// Ok, but easy to get wrongcopied :=make([]byte, len(old)) // Yet another way to copy; make new SAME LENGTH slice copy(copied, old) // Copy using the copy command (note it copies the minimum length of copied and old!)
copied =append(copied, add) // Then append to itself