注意,FAQ中提到了一致性
其次是一致性。如果该类型的一些方法必须具有指针接收器,那么其他方法也应该具有指针接收器,因此无论如何使用该类型,方法集都是一致的。有关详细信息,请参阅方法集部分。
如本文所述:
对于接收者来说,指针与值之间的规则是值方法可以
可以在指针和值上调用,但只能调用指针方法
在指针
正如Sart Simha所评论的那样,这是不正确的
值接收器和指针接收器方法都可以在正确类型的指针或非指针上调用。
不管对什么方法调用,在方法体中,当使用值接收器时,接收器的标识符指向逐拷贝值,当使用指针接收器时,接收器指向指针:示例。
Now:
有人能告诉我一个例子,一个值接收器明显比一个指针接收器更有意义吗?
代码评审注释可以帮助:
If the receiver is a map, func or chan, don't use a pointer to it.
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.
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.
Finally, when in doubt, use a pointer receiver.
粗体部分可以在net/http/server.go#Write()中找到:
// Write writes the headers described in h to w.
//
// This method has a value receiver, despite the somewhat large size
// of h, because it prevents an allocation. The escape analysis isn't
// smart enough to realize this function doesn't mutate h.
func (h extraHeader) Write(w *bufio.Writer) {
...
}
注意:irbull在评论中指出了一个关于接口方法的警告:
根据接收器类型应该一致的建议,如果你有一个指针接收器,那么你的(p *type) String()字符串方法也应该使用一个指针接收器。
但是这并没有实现Stringer接口,除非API的调用者也使用指向类型的指针,这可能是API的可用性问题。
我不知道这里的一致性是否胜过可用性。
指出:
方法集(指针vs值接收器)
您可以将方法与值接收器和方法与指针接收器混合和匹配,并将它们与包含值和指针的变量一起使用,而不用担心哪个是哪个。
两者都可以工作,而且语法是相同的。
然而,如果需要带有指针接收器的方法来满足接口,那么只能将指针赋值给接口——值将无效。
“Go接口和自动生成函数”,摘自Chris Siebenmann(2017年6月)
通过接口调用值接收器方法总是会创建值的额外副本。
接口值基本上是指针,而你的值接收器方法需要值;因此,每次调用都需要Go创建一个新的值副本,用它调用你的方法,然后扔掉这个值。
只要使用值接收器方法并通过接口值调用它们,就无法避免这种情况;这是围棋的基本要求。
“了解Go的不可寻址值和切片”(剧照来自克里斯(2018年9月))
不可寻址值的概念,与可寻址值相反。谨慎的技术版本是在Go规范的地址操作符中,但是简单的总结版本是大多数匿名值是不可寻址的(一个大的例外是复合字面量)