1

I'm starting with Go and I'm having difficulty understanding pointers and references to structs inside functions.

Considering the example https://play.golang.org/p/zd8La4ecNXw

package main

import "fmt"

type User struct {
  Name string
}

func main() {
  // 1st
  u := User{Name: "Anne"}
  fmt.Println("1st: ", &u.Name)
  fmt.Println("1st: ", u.Name)
  Modify1(u)
  fmt.Println("1st: ", u.Name)

  // 2nd
  fmt.Println()
  v := &User{Name: "Anne"}
  fmt.Println("2nd: ", &v.Name)
  fmt.Println("2nd: ", v.Name)
  Modify2(v)
  fmt.Println("2nd: ", v.Name)

  // 3rd
  fmt.Println()
  y := &User{Name: "Anne"}
  fmt.Println("3rd: ", &y.Name)
  fmt.Println("3rd: ", y.Name)
  Modify3(&y)
  fmt.Println("3rd: ", y.Name)

  // 4th
  fmt.Println()
  z := &User{Name: "Anne"}
  fmt.Println("4th: ", &z.Name)
  fmt.Println("4th: ", z.Name)
  Modify4(z)
  fmt.Println("4th: ", z.Name)
}

func Modify1(u User) {
  fmt.Println("func: ", &u.Name)
  u.Name = "Duncan"
  fmt.Println("func: ", u.Name)
}

func Modify2(v *User) {
  fmt.Println("func: ", &v.Name)
  v = &User{Name: "Paul"}
  fmt.Println("func: ", v.Name)
}

func Modify3(y **User) {
  fmt.Println("func: ", &y)
  fmt.Println("func: ", &(*y).Name)
  *y = &User{Name: "Bob"}
  fmt.Println("func: ", (*y).Name)
}

func Modify4(z *User) {
  fmt.Println("func: ", &z.Name)
  z.Name = "John"
  fmt.Println("func: ", z.Name)
}

Results:

1st:  0x40e128
1st:  Anne
func:  0x40e140
func:  Duncan
1st:  Anne

2nd:  0x40e158
2nd:  Anne
func:  0x40e158
func:  Paul
2nd:  Anne

3rd:  0x40e188
3rd:  Anne
func:  0x40e198
func:  0x40e188
func:  Bob
3rd:  Bob

4th:  0x40e1b8
4th:  Anne
func:  0x40e1b8
func:  John
4th:  John

Apart from the 1st example, where Ihave no questions, all the others seem to point to the original struct assignment, but 2nd doesn't change the caller value.

Why does this happen, and why is this different than in 3rd and 4th?

4
  • 2
    v is a local variable in Modify2. Commented Jan 31, 2019 at 14:58
  • 2
    It's a local variable in all of them, but in Modify2 you're just overwriting the local variable instead of overwriting the value it points to. Commented Jan 31, 2019 at 15:03
  • I've not used SO in the last years, came back and really don't understand why people downvote when a user doesn't understand something and shows an effort to understand. Commented Jan 31, 2019 at 15:32
  • @Adrian: thanks for the comment, but cannot understand why. Commented Jan 31, 2019 at 15:34

1 Answer 1

3

The key thing to remember is that everything is passed by value (a.k.a. passed by copying). When you pass a pointer to a function, that parameter is still a local variable in that function, containing a copy of the pointer. That local copy points to the same memory referenced by the caller.

When you do this:

v = &User{Name: "Paul"}

You're overwriting your local variable v with a new pointer to a new instance of User. It now points to different memory than the caller's pointer, so the caller sees nothing.

If you instead did this:

*v = User{Name: "Paul"}

Then the same pointer, pointing to the same memory, would be overwritten with a new instance of User.

Likewise, in Modify3, you have a pointer to a pointer. So your caller and function each have a local variable, which points to a memory location, which holds another memory location, where an actual value can be found. Note that this is a very unusual (but not unheard of) pattern in Go, but common in other languages. When you do this:

*y = &User{Name: "Bob"}

You're creating a new pointer to a new User, and storing that new pointer in the shared memory pointed to by the outer pointer. So, again, both function and caller are sharing memory and both will see the changes.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.