Best practice for Method Receivers

1,776 views
Skip to first unread message

st ov

unread,
Mar 24, 2017, 9:20:06 PM3/24/17
to golang-nuts
Is it idiomatic to mix-&-match pointer and value method receivers for a given type?
or in general, if a single method requires a pointer receiver than all methods should take a pointer, regardless if a value receiver is appropriate?

For example, should Foo.SetVal also be a pointer receiver even though a value receiver would be acceptable?
https://play.golang.org/p/rd_6BLol8O

type Foo struct{
   
ref []int
    val
int
}

// would not set if method receiver is value receiver (foo Foo)
func
(foo *Foo) SetRef(ref []int) {
    foo
.ref = ref
}
func
(foo Foo) SetVal(val int) {
    foo
.val = val
}

func main
() {
    foo
:= Foo{}
    foo
.SetRef([]int{1,2,3})
    foo
.SetVal(1)
   
    fmt
.Printf("%v",foo) // {[1 2 3] 0}
}



peterGo

unread,
Mar 25, 2017, 12:49:18 AM3/25/17
to golang-nuts
Frequently Asked Questions (FAQ)
Should I define methods on values or pointers?
https://golang.org/doc/faq#methods_on_values_or_pointers

If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used.

Peter

T L

unread,
Mar 25, 2017, 2:18:29 AM3/25/17
to golang-nuts


On Saturday, March 25, 2017 at 9:20:06 AM UTC+8, st ov wrote:
Is it idiomatic to mix-&-match pointer and value method receivers for a given type?
or in general, if a single method requires a pointer receiver than all methods should take a pointer, regardless if a value receiver is appropriate?

You should only use non-pointer receivers in two occasions:
1. the size of receiver type is small and the method doesn't modify the receiver (getter methods).
2. you really want to derive a new value from the receiver.

For example:

func (t T) Foo() Bar {
    return t.foo
}

func (t T) Clone() T {
   return t
}
 

Dragos Harabor

unread,
Mar 25, 2017, 10:51:52 PM3/25/17
to golang-nuts
You may also run into subtle races when you mix pointer and value receivers, as seen here:
https://dave.cheney.net/2015/11/18/wednesday-pop-quiz-spot-the-race

Val

unread,
Mar 27, 2017, 10:05:42 AM3/27/17
to golang-nuts
Hello st ov
What do you mean with "even though a value receiver would be acceptable" ?
Your sample code clearly shows that after foo.SetVal(1) , we still have foo.val == 0  which looks unacceptable to me (broken setter).

Is it possible that you are confusing  "is the method receiver a reference type" with "are the method arguments reference types" ?
In any case, when you want your method to modify the receiver, you must use a reference receiver (i.e. pointer), doing otherwise would be pointless (modifying a copy, which would immediately be discarded when method returns).

If you wonder about passing value arguments (e.g.  val int) or reference arguments (e.g.  ref []int) : both are possible and may be mixed.  But this is mostly unrelated to the pointerness of the receiver.

HTH
 Val

Ayan George

unread,
Mar 27, 2017, 10:27:24 AM3/27/17
to golan...@googlegroups.com


On 03/24/2017 09:20 PM, st ov wrote:
> Is it idiomatic to mix-&-match pointer and value method receivers for
> a given type? or in *_general_*, if a single method requires a
> pointer receiver than *all methods* should take a pointer, regardless
> if a value receiver is appropriate?
>
> For example, should Foo.SetVal also be a pointer receiver even though
> a value receiver would be acceptable?

IMHO this decision should be made exactly the way you would decide if
any other parameter to a function is a pointer. In fact, I think
function receivers are equivalent to passing an additional parameter to
a function. Someone please correct me if I'm wrong.

Particularly: Do you plan to modify the receiver and is the receiver so
large that copying it would be cumbersome.

I don't think there is a hard rule that if you make one receiver a
pointer then all of them must be (or vice versa).

-ayan

st ov

unread,
Apr 2, 2017, 5:57:59 PM4/2/17
to golang-nuts
Could you explain how this is a race condition?
Running it in the playground appears to succeed, https://play.golang.org/p/zs6T361fc6

Dragos Harabor

unread,
Apr 2, 2017, 6:04:45 PM4/2/17
to golang-nuts
You need to run it on your own machine with the -race flag, e.g.:
go run -race file.go

More info on the race detector:

Val

unread,
Apr 3, 2017, 9:49:02 AM4/3/17
to golang-nuts
It's a combination of :
1) A read and a write of a variable, happening without synchronization (see happens-before), is always a race, and always a bug, even when it "looks" innocuous.
2) When the receiver of a method is not a pointer, then this receiver is copied when the method is called.
3)  (*RPC) compute()  writes field result.
4)  (RPC) version()  copies the whole RPC value, which is a read to field result.

There is a data race on rpc.result

st ov

unread,
Apr 20, 2017, 12:36:27 PM4/20/17
to golang-nuts
Thanks! Really appreciate the explanation!
Reply all
Reply to author
Forward
0 new messages