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.
Bad | Good |
---|
- func (d Driver) SetTrips(trips []Trip) {
- d.trips = trips
- }
- trips := …
- d1.SetTrips(trips)
- // Did you mean to modify d1.trips?
- trips[0] = …
|
- 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 internalstate.
Bad | Good |
---|
- type Stats struct {
- sync.Mutex
-
- counters map[string]int
- }
-
- // Snapshot returns the current stats.
- func (s Stats) Snapshot() map[string]int {
- s.Lock()
- defer s.Unlock()
- return s.counters
- }
- // snapshot is no longer protected by the lock!
- snapshot := stats.Snapshot()
|
- type Stats struct {
- sync.Mutex
-
- counters map[string]int
- }
-
- func (s Stats) Snapshot() map[string]int {
- s.Lock()
- defer s.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()
|