Copy Slices and Maps at Boundaries

Slices and maps contain pointers to the underlying data so be wary of scenarioswhen they need to be copied.

Receiving Slices and Maps

Keep in mind that users can modify a map or slice you received as an argumentif you store a reference to it.

BadGood
  1. func (d Driver) SetTrips(trips []Trip) {
  2. d.trips = trips
  3. }
  4. trips :=
  5. d1.SetTrips(trips)
  6. // Did you mean to modify d1.trips?
  7. trips[0] =
  1. func (d Driver) SetTrips(trips []Trip) {
  2. d.trips = make([]Trip, len(trips))
  3. copy(d.trips, trips)
  4. }
  5.  
  6. trips :=
  7. d1.SetTrips(trips)
  8.  
  9. // We can now modify trips[0] without affecting d1.trips.
  10. trips[0] =

Returning Slices and Maps

Similarly, be wary of user modifications to maps or slices exposing internalstate.

BadGood
  1. type Stats struct {
  2. sync.Mutex
  3.  
  4. counters map[string]int
  5. }
  6.  
  7. // Snapshot returns the current stats.
  8. func (s Stats) Snapshot() map[string]int {
  9. s.Lock()
  10. defer s.Unlock()
  11. return s.counters
  12. }
  13. // snapshot is no longer protected by the lock!
  14. snapshot := stats.Snapshot()
  1. type Stats struct {
  2. sync.Mutex
  3.  
  4. counters map[string]int
  5. }
  6.  
  7. func (s Stats) Snapshot() map[string]int {
  8. s.Lock()
  9. defer s.Unlock()
  10.  
  11. result := make(map[string]int, len(s.counters))
  12. for k, v := range s.counters {
  13. result[k] = v
  14. }
  15. return result
  16. }
  17.  
  18. // Snapshot is now a copy.
  19. snapshot := stats.Snapshot()