18

Why when we reference struct using (*structObj) does Go seem to return a new copy of structObj rather than return the same address of original structObj? This might be some misunderstanding of mine, so I seek clarification

package main

import (
    "fmt"
)

type me struct {
    color string
    total int
}

func study() *me {
    p := me{}
    p.color = "tomato"
    fmt.Printf("%p\n", &p.color)
    return &p
}

func main() {
    p := study()
    fmt.Printf("&p.color = %p\n", &p.color)

    obj := *p
    fmt.Printf("&obj.color = %p\n", &obj.color)
    fmt.Printf("obj = %+v\n", obj)

    p.color = "purple"
    fmt.Printf("p.color = %p\n", &p.color)
    fmt.Printf("p = %+v\n", p)
    fmt.Printf("obj  = %+v\n", obj)

    obj2 := *p
    fmt.Printf("obj2 = %+v\n", obj2)
}

Output

0x10434120
&p.color = 0x10434120
&obj.color = 0x10434140   //different than &p.color!
obj = {color:tomato total:0}
p.color = 0x10434120
p = &{color:purple total:0}
obj  = {color:tomato total:0}
obj2 = {color:purple total:0} // we get purple now when dereference again

Go playground

Dave C
  • 7,729
  • 4
  • 49
  • 65
ken
  • 13,869
  • 6
  • 42
  • 36

3 Answers3

23

When you write

obj := *p

You are copying the value of struct pointed to by p (* dereferences p). It is similar to:

var obj me = *p

So obj is a new variable of type me, being initialized to the value of *p. This causes obj to have a different memory address.

Note that obj if of type me, while p is of type *me. But they are separate values. Changing a value of a field of obj will not affect the value of that field in p (unless the me struct has a reference type in it as a field, i.e. slice, map or channels. See here and here.). If you want to bring about that effect, use:

obj := p
// equivalent to: var obj *me = p

Now obj points to the same object as p. They still have different addresses themselves, but hold within them the same address of the actual me object.

Community
  • 1
  • 1
abhink
  • 8,740
  • 1
  • 36
  • 48
  • is there a way to get the same dereferenced pointer to p? as in the main() func, if we are appending the struct as a slice, we will always have to dereference it inside the append itself, i.e. res = append(res, *p). – ken Jul 18 '16 at 18:39
  • It not just about "creating" a new variable, assignment to an existing variable via a dereference copies the value, e.g. `*a = *b` still copied copies `*b` to `*a`. – JimB Jul 18 '16 at 18:39
  • @jimB yap, is there a way to avoid the repeated copy? as basically the operation just need to deal with the same p struct. – ken Jul 19 '16 at 08:44
  • 2
    @ken: I don't understand what you mean. You need some sort of value, so it's either going to be the struct value or a pointer value. If you want to reference the same struct or avoid copying you use a pointer. – JimB Jul 19 '16 at 12:47
  • @JimB okay, maybe i am thinking too much ;) I got what you mean, thanks for your replied! – ken Jul 19 '16 at 14:03
18

No, "assignment" always creates a copy in Go, including assignment to function and method arguments. The statement obj := *p copies the value of *p to obj.

If you change the statement p.color = "purple" to (*p).color = "purple" you will get the same output, because dereferencing p itself does not create a copy.

JimB
  • 104,193
  • 13
  • 262
  • 255
  • 1
    in fact, coming from c background, this make me confuse. Thanks, think both answer are correct and reach me about the same time, so chose a more details, but upvote this for your answer too! – ken Jul 18 '16 at 18:34
  • 3
    `dereferencing p itself does not create a copy` this clarified my confusion, thanks – stackoverflower Apr 13 '17 at 10:34
6

tl;dr Dereferencing (using the * operator) in Go does not make a copy. It returns the value the pointer points to.

Michael Dorner
  • 17,587
  • 13
  • 87
  • 117
  • 1
    True, although an *assignment does copy*! (Note that for slices and maps it will only copy the header value, not the actual elements, so it may seem like it does not copy) – Allen Hamilton Jan 23 '20 at 23:45