困惑

定义结构方法时的常见困境,你的方法接收器应该使用指针接收器还是值接收器?

1
2
3
4
5
type T struct {
    a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver

什么时候使用指针接收器

修改接收器

如果要在方法中更改接收器的状态,操纵它的值,请使用指针接收器。 使用值接收器是不可能的,它按值复制(对值接收器的任何修改都是该副本的本地修改)。

优化

如果您定义方法的结构非常大,复制它将比使用值接收器代价更大。

值接收器在原始类型值的副本上运行,这意味着涉及成本,特别是如果结构非常大,并且接收的指针更有效。

什么时候使用值接收器

  • 如果您不需要编辑接收器值,请使用值接收器。
  • 值接收器是并发安全的,而指针接收器不是并发安全的。

有一种情况,您可能希望将指针接收器用于通常使用值接收器的方法,并且当您在该类型上定义了其他指针接收器时,为了保持一致性,您应该在所有方法中使用指针接收器。

Go CodeReview 注释

选择是否在方法上使用值或指针接收器可能很困难,尤其是对于新的Go程序员。

如果有疑问,请使用指针,但有时候值接收器是有意义的,通常是出于效率的原因,例如小的不变结构或基本类型的值。

一些有用的指导:

  • 如果接收器是 map,func 或 chan,则不要使用指向它们的指针。如果接收器是切片并且该方法不重新切片或重新分配切片,则不要使用指向它的指针。
  • 如果该方法需要改变接收器,则接收器必须是指针。
  • 如果接收器是包含sync.Mutex或类似同步字段的结构,则接收器必须是避免复制的指针。
  • 如果接收器是大型结构或数组,则指针接收器更有效。有多大?假设它相当于将所有元素作为参数传递给方法。如果感觉太大,对接收器来说也太大了。
  • 函数或方法可以同时或在从这种方法调用时改变接收器吗?在调用方法时,值类型会创建接收器的副本,因此外部更新将不会应用于此接收器。如果必须在原始接收器中看到更改,则接收器必须是指针。
  • 如果接收器是结构,数组或切片,并且其任何元素是指向可能变异的东西的指针,则更喜欢指针接收器,因为它将使读者更清楚地意图。
  • 如果接收器是一个小数组或结构,它自然是一个值类型(例如,类似于 time.Time 类型),没有可变字段和没有指针,或者只是一个简单的基本类型,如 int 或 string,值接收者是有道理的,值接收器可以减少生成的垃圾量;如果将值传递给值方法,则可以使用堆栈上的副本而不是在堆上进行分配。(编译器试图避免这种分配,但它不能总是成功。)不要为此而选择值接收器类型而不先进行分析。
  • 最后,如有疑问,请使用指针接收器。

参考资料

  1. receiver-type

茶歇驿站

一个可以让你停下来看一看,在茶歇之余给你帮助的小站,这里的内容主要是后端技术,个人管理,团队管理,以及其他个人杂想。