Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a method with a pointer receiver still work when it receives a value?

I was just playing with Exercise 51 in the Tour of Go. The explanation claims the Scale method has no effect when it receives a Vertex instead of a pointer to a Vertex.

Yet when I change the declaration v := &Vertex{3, 4} to v := Vertex{3, 4} in main the only change in the output is the missing & to mark the pointer.

So why does Scale change the variable it receives even if the variable isn't a pointer?

like image 312
sjakobi Avatar asked Aug 01 '13 14:08

sjakobi


2 Answers

It does not "receive" a value. Go is strongly typed, so if somewhere a pointer to T is prescribed, a pointer to T (*T) is the only option which can happen as a value for such typed place.

The "magic" is in the compiler which effectively "rewrites" your code under certain conditions:

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():

Related: Method sets

like image 59
zzzz Avatar answered Sep 28 '22 19:09

zzzz


The difference that the tour suggests isn't actually in changing v := &Vertex{3, 4} to v:= Vertex{3, 4}, but rather in changing the definitions of the two methods so that they work on values instead of pointers. So, for example, for Scale, func (v *Vertex) Scale(f float64) {... becomes func (v Vertex) Scale(f float64) {... (note that (v *Vertex), a pointer value, becomes (v Vertex), a non-pointer value). In both cases, you should leave the declaration of v as v := &Vertex{3, 4}.

You'll notice that in the first case, when the methods take pointers, the output is &{15 20} 25. However, when the methods take values rather than pointers, the output is &{3 4} 5.

In both cases, v is a pointer to a Vertex object. In the first case, the pointer is passed to the methods, and everything works as expected - any modifications made to the Vertex object are made to the original value, so those changes persist after the method returns. In the second case, though v is still a pointer, the Go compiler is smart enough to convert v.Scale(5) to (*v).Scale(5), where v is dereferenced, and the resulting value is passed to Scale.

like image 36
joshlf Avatar answered Sep 28 '22 18:09

joshlf