情谊提醒:此篇文章大概须要浏览 3 分钟 49 秒,不足之处请多指教,感谢您的浏览。订阅本站
译文原地址:Should methods be declared on T or *T – David
在 Go 中,对于任何的类型 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 上申明办法。另一种说法是,类型上的办法被申明为接收器接收者值的正本,或一个指向其接收者值的指针。所以问题就存在了,到底是哪种模式最合适?
显然,如果你的办法扭转了他的接收者,他应该在 *T 上申明。然而,如果办法不扭转他的接收者,在 T 上申明它是平安的么?
事实证明,这样做的话平安的状况十分无限(简略了解就是不平安的)。例如,家喻户晓,你不应该复制一个 sync.Mutex 的值,因为它突破了互斥量的不变量。因为互斥锁管制对变量(共享资源)的拜访,他们常常被包装在一个构造体中,蕴含他们的管制的值(共享资源):
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
}
大部分 Gopher 都晓得,遗记在指针接收器 *Val 上是申明 Get 或 Add 办法是谬误的。然而,任何嵌入 Val 来利用其 0 值的类型,也必须仅在其指针接收器上申明办法,否者可能会无心复制其嵌入类型值的内容:
type Stats struct {a, b, c counter.Val}
func (s Stats) Sum() int {return s.a.Get() + s.b.Get() + s.c.Get() // whoops(哎呀)}
保护值切片的类型可能会呈现相似的陷阱,当然也有可能发生意外的数据竞争。
简而言之,我认为您更应该喜爱在 *T 上申明办法,除非您有十分充沛的理由不该这样做。
- 咱们说 T 但这只是您申明的类型的占位符;
- 此规定是递归的,取
*T
类型的变量的地址返回的是**T
类型的后果; - 这就是为什么没有人能够在像 int 这样的根底类型上申明办法;
- Go 中的办法只是将接受者作为第一个模式参数传递的函数的语法糖;
- 如果办法不扭转它的接收者,它是否须要是一个办法吗?
相干文章:
- What is the zero value, and why is it useful?
- Ice cream makers and data races
- Slices from the ground up
- The empty struct
最初,此篇文章我是第一次尝试翻译英文文章,只管英文程度不太好,一些单词不意识,然而置信本人翻译一篇文章能够学习英语与了解 Go 设计获取 double 的乐趣。