[译] 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
相关文章
相关标签/搜索