[譯] Go:方法接收者應該使用 T 仍是 *T

這篇文章是我幾天前在 Twitter 上提出的建議的延續。git

在 Go 中,對於任何類型 T 都存在類型 *T,表示獲取 T 類型(T 表示你聲明的類型)變量的地址。例如:程序員

type T struct { a int; b bool }
var t T    // t's type is T
var p = &t // p's type is *T
複製代碼

這兩種類型,T 和 *T 是不一樣的,*T 不能替代 T(此規則是遞歸的,**T 會返回 *T 地址指向的值)。github

你能夠在任何類型上聲明方法;也就是說,你在 package 中聲明瞭一個類型。所以,你能夠在這個類型上聲明一個方法,他的接收者可使用 T 或者 *T。或者是說聲明接收者的類型爲 T 是爲了獲取接收者值的副本,聲明接收者的類型爲 *T 是爲了獲取指向接收者值的指針(Go 中的方法只是函數的語法糖,它將接收者做爲第一個形式參數傳遞)。那麼問題就變成了,咱們應該選擇哪一種方式?(若是該方法不改變它的接收者,它是否須要方法這種形式?)golang

顯然,若是你的方法改變了接收者,那應該聲明 * T。可是,若是該方法不改變其接收者,是否能夠將其聲明爲 T 呢?函數

事實證實,這樣作的場景很是有限。例如,衆所周知,你不該該複製 sync.Mutex 值,由於它會使互斥鎖失效。因爲互斥鎖控制對數據的訪問,它們常常被包含在結構中:oop

package counter

type Val struct {
        mu  sync.Mutex
        val int
}

func (v *Val) Get() int {
        v.mu.Lock()
        defer v.mu.Unlock()
        return v.val
}

func (v *Val) Add(n int) {
        v.mu.Lock()
        defer v.mu.Unlock()
        v.val += n
}
複製代碼

大多數 Go 程序員都知道咱們應該使用指針接收者 *Val ,並在其上聲明 Get 或 Add 方法。可是,任何嵌入 Val 以利用其零值的類型,也必須把方法的接收者設爲指針類型,不然它可能複製其嵌入類型的值。ui

type Stats struct {
        a, b, c counter.Val
}

func (s Stats) Sum() int {
        return s.a.Get() + s.b.Get() + s.c.Get() // whoops
}
複製代碼

對於切片類型,可能也會發生相似的陷阱,固然也有可能發生意外的數據競爭spa

簡而言之,我認爲你更應該在 *T 上聲明方法,除非你有充分的理由不這樣作。.net

相關文章

  1. What is the zero value, and why is it useful?
  2. Ice cream makers and data races
  3. Slices from the ground up
  4. The empty struct
相關文章
相關標籤/搜索