Consider the following simple program, where we define a method on a pointer to the struct Vertex
and then invoke it with a pointer.
package main
import (
"fmt"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Mutate() {
v.X = 8
}
func main() {
v := &Vertex{3, 4}
v.Mutate()
fmt.Println(v.X)
}
The output of this program is 8
, which we expect because we are passing a pointer to a method which takes a pointer.
However, the following invocation also has an output of 8.
func main() {
v := Vertex{3, 4}
v.Mutate()
fmt.Println(v.X)
}
Symmetrically, if we redefine the method Mutate
to take a Vertex
instead of a pointer, then the mutation fails regardless of whether a pointer or a struct is passed.
This behavior seems to imply that whether the argument v
or a pointer to v
to passed depends entirely on the definition of the method, rather than on what is actually being passed.
Is this a correct interpretation, and will this always be the case? If not, what is the correct explanation for this behavior?
This behavior seems to imply that whether the argument v or a pointer to v to passed depends entirely on the definition of the method, rather than on what is actually being passed.
Is this a correct interpretation, and will this always be the case? If not, what is the correct explanation for this behavior?
No, this is not correct but it is almost correct.
What gets passed to your method is a pointer, even if it looks like v.Mutate()
with v
a non-pointer. The reason is: Your v
is "addressable" and thus its method set includes methods on the pointer type. See http://golang.org/ref/spec#Calls for details. This is a kind of syntactic sugar. Because v
is addressable you may take its address &v
and on this pointer you may invoke your method like (&v).Mutate
. This syntax is clumsy and Go automatically does it for you for addressable expressions.
(If you are interested in such stuff, why not read the whole language specification? It can be done in 3 hours.)