Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reassigning in pointer method receiver

Tags:

What I understand about pointer method receiver and non-pointer method receiver is first one can be modified in the method and next one isn't.

So, following worked exactly as I expected.

type student struct {     name string     age  int }  func (s *student) update() {     s.name = "unknown"     s.age = 0 }  func main() {     s := student{"hongseok", 13}     fmt.Println(s)      s.update()     fmt.Println(s) } 

It prints hongseok/13 and unknown/0.

But, I want to replace whole s in update method at once with reassigning. So, I've just altered update method as bellow.

func (s *student) update() {     s = &student{"unknown", 0} } 

And it doesn't change s in main method and prints double hongseok/13.

func (s *student) update() {     *s = student{"unknown", 0} } 

Above change fix the problem.

I think there's no semantic difference. What am I missing?

like image 758
Hongseok Yoon Avatar asked Jun 29 '16 16:06

Hongseok Yoon


People also ask

When would you use a pointer receiver?

There are two reasons to use a pointer receiver. The first is so that the method can modify the value that its receiver points to. The second is to avoid copying the value on each method call. This can be more efficient if the receiver is a large struct, for example.

Is it possible to call methods that are declared with a value receiver using a pointer?

you can mix and match methods with value receivers and methods with pointer receivers, and use them with variables containing values and pointers, without worrying about which is which. Both will work, and the syntax is the same.

Can you define a pointer to pointer in go?

A pointer to a pointer is a form of chain of pointers. Normally, a pointer contains the address of a variable. When we define a pointer to a pointer, the first pointer contains the address of the second pointer, which points to the location that contains the actual value as shown below.


2 Answers

In the first example:

func (s *student) update() {     s = &student{"unknown", 0} } 

You are assigning an entirely new "pointer value" to s, and the new *s points at a new student value. The variable s is scoped only to the method body, so there are no side effects after this returns.

In the second example

func (s *student) update() {     *s = student{"unknown", 0} } 

You are dereferencing s, and changing the value of *s to point to a new student value, or to put it differently, you are putting a new student value at the address where s points.

like image 113
JimB Avatar answered Oct 05 '22 08:10

JimB


In this example you're changing the address that is stored in s to a different value;

func (s *student) update() {     s = &student{"unknown", 0} } 

While using a pointer is regarded as 'passing by reference' the reference itself is a value like any other that is pushed onto the call stack. When you return to main, the value of s is whatever it was in that scope. So to give something more concrete, you called main with s = 1 (calling the addresses 1 and 2 for simplicity), in the method you allocate a new student located at address 2 and you set s = 2, when you return that version of s is popped from the stack and the s in main points to 1 which is unchanged.

In this latter example;

func (s *student) update() {     *s = student{"unknown", 0} } 

You're dereferencing s and assigning a new object to that location, overwriting the existing memory. When you return the pointer in main is still pointing to the same location but you have different data at that location in memory. So in this example you're writing a new student instance to address 1 so when you return you see the new value in the calling scope.

like image 20
evanmcdonnal Avatar answered Oct 05 '22 07:10

evanmcdonnal