- 原文地址:dave.cheney.net/2016/03/19/…
- 原文做者:Dave Cheney
- 譯文地址:github.com/watermelo/d…
- 譯者:咔嘰咔嘰
- 譯者水平有限,若有翻譯或理解謬誤,煩請幫忙指出
這篇文章是我幾天前在 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