Go 语言中方法的接收者到底该选择哪种类型,对于这个问题有个不是万金油的万金油,就是都使用指针类型的接收者。但凡事总有例外,如果能够准确使用方法的接收者类型,可以帮助我们更好的理解代码以及减少不必要的麻烦,同时提升代码运行的效率。

以下是 Go 官方 Wiki 中的一些建议:

  • If the receiver is a map, func or chan, don’t use a pointer to them. If the receiver is a slice and the method doesn’t reslice or reallocate the slice, don’t use a pointer to it.

    如果接收者是映射、函数或者通道,就不能使用指针类型。如果接收者是切片,并且对应的方法没有进行重新切片或是重新分配切片内存的操作,那么也不要使用指针类型。

  • If the method needs to mutate the receiver, the receiver must be a pointer.

    如果方法需要修改接收者,那么接收者的类型必须是指针。

  • If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.

    如果接收者是一个包含了 sync.Mutex 或类似的同步字段的结构体,那么接收者必须是指针来避免冗余的副本拷贝。

  • If the receiver is a large struct or array, a pointer receiver is more efficient. How large is large? Assume it’s equivalent to passing all its elements as arguments to the method. If that feels too large, it’s also too large for the receiver.

    如果接收者是一个体量很大的结构体或数组,指针类型的接收者会更高效。不过到底多大才算大呢?假设这就像是把它所有的元素像参数一样传给方法。如果感觉这个参数会太大了,那么对接收者来说同样也太大了。

  • Can function or methods, either concurrently or when called from this method, be mutating the receiver? A value type creates a copy of the receiver when the method is invoked, so outside updates will not be applied to this receiver. If changes must be visible in the original receiver, the receiver must be a pointer.

    函数或方法可以在并发或被方法调用时修改接收者吗?对于值类型来说会在方法被调用时创建一份接收者的副本,因此来自外部的更新并不会影响到这个接收者。如果发生的修改要在原接收者身上反映出来,那么接收者必须是一个指针。

  • If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.

    如果接收者是结构体、数组或是切片,并且它们之中的指针元素所指向的内容有可能被修改的话,那么选择指针类型的接收者会更好一些,因为这样可以让方法的使用意图更加明确和可读。

  • If the receiver is a small array or struct that is naturally a value type (for instance, something like the time.Time type), with no mutable fields and no pointers, or is just a simple basic type such as int or string, a value receiver makes sense. A value receiver can reduce the amount of garbage that can be generated; if a value is passed to a value method, an on-stack copy can be used instead of allocating on the heap. (The compiler tries to be smart about avoiding this allocation, but it can’t always succeed.) Don’t choose a value receiver type for this reason without profiling first.

    如果接收者是一个很小的数组或是结构体,像是一些普通的值类型(比如 time.Time 类型),没有可变的字段和指针,或者就是一些很简单的基础类型像是 intstring,那么值类型的接收者会更加合理一些。因为值类型接收者可以减少垃圾生成的数量;在向值类型接收者方法中传入一个值时,编译器会采用栈上复制而不是在堆上进行分配。(编译器会尽量避免内存分配,但也不是总能做得到的)但也不要因为这个原因,就不进行事先的分析而盲目地使用值类型的接收者。

  • Finally, when in doubt, use a pointer receiver.

    最后,如果还是存在疑惑,那就直接使用指针类型的接收者吧。

参考