slice 和 map 包含了指向底层数据的指针,因此在需要复制它们时要特别注意。

在 Go 源码文件 src/runtime/slice.go 我们可以找到切片的底层数据结构 runtime.slice

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

在 Go 源码文件 src/runtime/map.go 我们可以找到 map 底层核心数据结构 runtime.hmap

// A header for a Go map.
type hmap struct {
    // Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
    // Make sure this stays in sync with the compiler's definition.
    count     int // # live cells == size of map.  Must be first (used by len() builtin)
    flags     uint8
    B         uint8  // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
    noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
    hash0     uint32 // hash seed

    buckets    unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
    oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
    nevacuate  uintptr        // progress counter for evacuation (buckets less than this have been evacuated)

    extra *mapextra // optional fields
}

// mapextra holds fields that are not present on all maps.
type mapextra struct {
    // If both key and elem do not contain pointers and are inline, then we mark bucket
    // type as containing no pointers. This avoids scanning such maps.
    // However, bmap.overflow is a pointer. In order to keep overflow buckets
    // alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow.
    // overflow and oldoverflow are only used if key and elem do not contain pointers.
    // overflow contains overflow buckets for hmap.buckets.
    // oldoverflow contains overflow buckets for hmap.oldbuckets.
    // The indirection allows to store a pointer to the slice in hiter.
    overflow    *[]*bmap
    oldoverflow *[]*bmap

    // nextOverflow holds a pointer to a free overflow bucket.
    nextOverflow *bmap
}

在接收 slice 和 map 时,请记住当 map 或 slice 作为函数参数传入时,如果您存储了对它们的引用,则用户可以对其进行修改。

// Bas
func (d *Driver) SetTrips(trips []Trip) {
    d.trips = trips
}

trips := ...
d.SetTrips(trips)

// 你是要修改 d.trips 吗?
trips[0] = ...

// Good
func (d *Driver) SetTrips(trips []Trip) {
  d.trips = make([]Trip, len(trips))
  copy(d.trips, trips)
}

trips := ...
d.SetTrips(trips)

// 这里我们修改 trips[0],但不会影响 d.trips
trips[0] = ...

在返回 slice 和 map 时,同样的,请注意用户对暴露内部状态的 map 或 slice 的修改。

// Bad
type Stats struct {
  mu sync.Mutex

  counters map[string]int
}

// Snapshot 返回当前状态。
func (s *Stats) Snapshot() map[string]int {
  s.mu.Lock()
  defer s.mu.Unlock()

  return s.counters
}

// snapshot 不再受互斥锁保护,可能会引发并发读写的错误
snapshot := stats.Snapshot()

// Good
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 现在是一个拷贝
snapshot := stats.Snapshot()
powered by Gitbook该文章修订时间: 2024-03-22 15:30:00

results matching ""

    No results matching ""