简体   繁体   中英

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?

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 xm() 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 , xm() is shorthand for (&x).m() :

Related: Method sets

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM