Copy Slices and Maps at Boundaries
Slices and maps contain pointers to the underlying data so be wary of scenarios when they need to be copied.
Receiving Slices 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.
| Bad | Good |
|---|---|
| ```go func (d *Driver) SetTrips(trips []Trip) { d.trips = trips } trips := ... d1.SetTrips(trips) // Did you mean to modify d1.trips? trips[0] = ... ``` | ```go func (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.
| Bad | Good |
|---|---|
| ```go type Stats struct { 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() ``` | ```go type Stats struct { 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() ``` |