重新分配指针方法接收器

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?

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.

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.

I think, your main problem is that you don't understand well neither one of two concepts that appear in your question.

Let start with pointers. When you don't use pointers, assigning of value means create a simple copy of previous value. The new value is not bound any way with previous one. That means if you change the old value or new, it does not influence the second one. This is normal for primitive types (like ints, bool, string) and structures.

a := 1
b := a
a++   // a was changed, but value of b does not change at all

And now the pointers - it is something that points to some space into memory. For simplicity we create two pointers and both will point to same place.

package main

import (
    "fmt"
)

func main() {
    type student struct {
        name string
        age  int
    }
    p1 := &student{"hongseok", 13} // this mean create a new student, but we want only address of student, not value
    p2 := p1
    (*p1).age = 15      // because p1 and p2 point to same memory (*p2).age also changed
    fmt.Println("1.", p2.age) // 15
    p2.age = 32         // in golang I could write (*p2).ago or p2.ago this is the same
    fmt.Println("2.", p1.age) // 32
    fmt.Println("3.",p1, "==", p2)
    fmt.Printf("4. %p == %p
", p1, p2)
    // But now I force point p2 to new place
    p2 = &student{"another student", 12}
    fmt.Println("5.", p1, "!=", p2)
    fmt.Printf("6. %p == %p
", p1, p2)
    p1.age = 14    // does it influce p2.age? no! Why? Because it is in different address
    fmt.Println("7.", p1, "!=", p2)
        // but could is somehow force that point to same address ? Of course
    p2 = p1 // p2 will point to same place as p1 and old value of p2 will be freed by garbage collector 
}

And don't be confused - pointer can also points named value.

a := 42  // means allocate memory for integer, and we give that memory name "a" 
p := &a
*p++     // it change value of a
a = 5    // and of course this change value of *p

And now back to methods, that receiver is/is not a pointer. If receiver of method is pointer, that means you can change it value - the same way as I did few lines ago.

If method receiver is not a pointer, that means - before calling a method, it will be created a copy of struct and method will be called on that copy. You of course can change value of copy, but it does not influence original value.