Is it acceptable/valid practise to create mixed receivers on a type? As shown below bind is pointer receiver because it is mutating the type but validate is a copy receiver as it is not mutating the type. Although it would work, is it idiomatic Go?
Thanks
type Request struct {
Name string
Price int
}
func (r *Request) bind(name string) error {
r.Name = name
}
func (r Request) validate() error {
if r.Name == "yow" {
return some error
}
return nil
}
This works, and I don’t think this is against any idioms.
However, there is a catch. If you add an interface like this one:
type Requester interface {
bind(string) error
validate() error
}
and a function
func iWantARequester(r Requester) {}
then you cannot pass a variable of type Request to the function. You would get an error saying,
“Request does not implement Requester (bind method has pointer receiver)”
Why? Because method bind() cannot be called on a type Request. You need a pointer type here, &Request, so that bind() can access the receiver through the pointer.
The non-pointer type Request “owns” only one of the two methods, validate().
A pointer type &Request, on the other hand, owns both methods because it is able to follow the pointer receiver in bind() as well as access the non-pointer receiver in validate().
I am not sure if the explanation is clear enough. Here is a runnable code in the playground to show the difference. Run this code and get an error at iWantARequester(r). Comment out the code around r and you’ll see that pr works fine.
And here is a link to the specification of method sets that explains the situation this way:
The method set of a defined typeT consists of all methods declared with receiver type T.
The method set of a pointer to a defined type T (where T is neither a pointer nor an interface) is the set of all methods declared with receiver *T or T.
(emphasis mine)
T would be Request in your code, and “a pointer to a defined type T” would be &Request.
The point that Dave Cheney makes is that a method with a non-pointer receiver might inadvertently copy a value that is not supposed to be copied, such as a struct containing a mutex.