- 原文地址: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