为什么接收者在Go中按值传递?

时间:2013-08-26 01:37:15

标签: go

好像你总是想要这个:

func (self *Widget) Do() {
}

而不是

func (self Widget) Do() {
}

如果是这样,那么通过使用后一种语法来获得前一个语义 OUGHT 的方法。即接收者应该通过引用传递。

3 个答案:

答案 0 :(得分:26)

这是因为everything in Go is pass by value。这使得它与其他C族语言保持一致,并且意味着您永远不需要记住您正在查看的情况是否通过值传递。

从该链接:

  

与C系列中的所有语言一样,Go中的所有内容都按值传递。也就是说,函数总是获取正在传递的东西的副本,就好像有一个赋值语句将值赋给参数。例如,将int值传递给函数会生成int的副本,并且传递指针值会生成指针的副本,但不会生成指向的数据。 (有关它如何影响方法接收器的讨论,请参阅下一节。)

然后:

  func (s *MyStruct) pointerMethod() { } // method on pointer
  func (s MyStruct)  valueMethod()   { } // method on value
     

对于不习惯指针的程序员来说,这两个例子之间的区别可能令人困惑,但实际上情况非常简单。在类型上定义方法时,接收器(上例中的s)的行为就像它是方法的参数一样。然后,将函数参数定义为值还是指针,将接收器定义为值还是指针都是同一个问题。有几点需要考虑。

     

首先,最重要的是,该方法是否需要修改接收器?如果是,接收器必须是指针。 (切片和贴图充当引用,因此它们的故事更加微妙,但是例如在接收方必须仍然是指针的方法中更改切片的长度。)在上面的示例中,如果pointerMethod修改了字段s,调用者将看到这些更改,但是使用调用者参数的副本调用valueMethod(这是传递值的定义),因此对调用者所做的更改将对调用者不可见。

     

顺便说一句,指针接收器与Java中的情况相同,尽管在Java中,指针隐藏在封面下; Go的价值接收器是不寻常的。

     

其次是效率的考虑。如果接收器很大,例如一个大的结构,使用指针接收器会便宜得多。

     

接下来是一致性。如果该类型的某些方法必须具有指针接收器,则其余方法也应如此,因此无论使用何种类型,方法集都是一致的。有关详细信息,请参阅方法集部分。

     

对于基本类型,切片和小结构等类型,值接收器非常便宜,因此除非方法的语义需要指针,否则值接收器是高效且清晰的。

答案 1 :(得分:6)

有时你想要通过引用传递。

的语义
func (self Widget) Get() Value {
}

如果您有一个小的不可变对象,则可能很有用。调用者可以确定该方法不会修改它的接收者。如果接收者是一个没有先读取代码的指针,就不能知道这个。

为了扩展它,例如

// accessor for things Config
func (self Thing) GetConfig() *Config {
}

通过查看此方法,我可以知道GetConfig总是会返回相同的Config。我可以修改该配置,但我无法在Thing中修改指向Config的指针。它非常接近Thing内部的const指针。

答案 2 :(得分:2)

  

好像你似乎总是想要这个:

没有。值接收器更通用。它可以用在指针接收器可以的所有位置;但是指针接收器不能在值接收器可以使用的所有位置使用 - 例如,如果你有一个Widget类型的右值表达式;你可以在上面调用值接收器方法,但不能使用指针接收器方法。