4
votes

The "Effective Go" documentation says the following.

The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.

http://tip.golang.org/doc/effective_go.html#pointers_vs_values

As such, if I define a method like the following, wouldn't it not be callable with a value?

func (self *someStruct) Change(newNum int) {
    self.propertyOne = newNum
}

However, the following still seems to work.

structInstance := &someStruct{
    propertyOne: 41,
}
(*structInstance).Change(456)

Why?

Is it converting the value (*structInstance) back into an address/pointer for the Change call?

How do I ensure that some instance of a type cannot call a method defined on the pointer (like Change)?

Go Playground Demo

http://play.golang.org/p/azbpp_djlG

2

2 Answers

3
votes

When a function is defined on a pointer receiver, Go will automatically convert a value to a pointer for that function.

type Cookies struct {
    Num int
}

func (c *Cookies) Buy(n int) {
    c.Num += n
}

func main() {
    c := Cookies{}
    c.Buy(10) //is equal to (&c).Buy(10)
    fmt.Println(c)
}

I can't find the reference to where that is defined right now, but I know it's in the official docs somewhere in the specs.

Also a side note, please don't use self or this in Go.

//edit

From http://tip.golang.org/ref/spec#Calls:

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

1
votes

From the language specification:

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

In your example, there is no Change method in (*structInstance)'s method set, but it is addressable and the method exists in &(*structInstance)'s method set, so the call is interpreted as (&(*structInstance)).Change(456), or more simply structInstance.Change(456).

The only way to prevent this behaviour would be to also define the Change method on someStruct, perhaps making it panic. That's not ideal though, since it will only tell you about the problem at runtime. It would be less confusing to structure your program so that the use of this shorthand doesn't actually matter.