好像你总是想要这个:
func (self *Widget) Do() {
}
而不是
func (self Widget) Do() {
}
如果是这样,那么通过使用后一种语法来获得前一个语义 OUGHT 的方法。即接收者应该通过引用传递。
答案 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
类型的右值表达式;你可以在上面调用值接收器方法,但不能使用指针接收器方法。