-2

I had deep dived in some comparisons about deep or shallow copy while passing a struct with primitive and pointer fields. Like:

type Copy struct {
    age int
    ac  *AnotherCopy
}

type AnotherCopy struct {
    surname string
}

func main() {
    s := Copy{
        age: 20,
        ac:  &AnotherCopy{surname: "Relic"},
    }
    passIt(&s)
    fmt.Printf("main s: %p\n", &s)
}

func passIt(s *Copy) {
    f := *s
    fmt.Printf("s: %p\n", &*s)
    fmt.Printf("f: %p\n", &f)

    f.age = 26
    f.ac.surname = "Walker"
    fmt.Printf("%v %s\n", f, f.ac.surname)
    fmt.Printf("%v %s\n", *s, s.ac.surname)
}

The result is

s: 0xc000010230
f: 0xc000010250
{26 0xc000010240} Walker
{20 0xc000010240} Walker
main s: 0xc000010230

What I can see here is that, when we pass a struct with primitive and composite types, It copies deeply primitive types and copies shallowly pointer (reference) fields.

I have read some articles about that process and there is a conflict between thoughts.

The question is what should we call that process? Deep copy or shallow copy? and if we call the process as only shallow copy, is this wrong? Can you clarify me please?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • 3
    There is no “deep copy” here, all assignment in go are a simple, shallow copy. We call that process a “copy” – JimB Dec 11 '21 at 20:39
  • Ok then, why the change on age field does not reflect to original variable. Does the shallow copy means that change on f's field should change s's field as well like explained here: https://stackoverflow.com/a/184780/11156237 "Later modifications to the contents of either are instantly reflected in the contents of other, as they share contents." – Serhat Yılmaz Dec 11 '21 at 20:56
  • 2
    after copy op, you end wth two values containing a pointer to the same surname value. When you derefrence surname to change/read its value, you access the memory adressed by the pointer that both struct instances share. there is nothing to clarify here. This is how it is designed. –  Dec 12 '21 at 09:15

1 Answers1

-2

In your question you mention "primitive and composite types" as being different, and I think that is the root of your confusion here. Go does not differentiate between primitive and composite types, but between pointer types and value types. "Primitive" types (int, int32, float64, etc.) are value types, but so are structs (and also arrays). Pointers to other types, maps, slices, channels, and interfaces are all pointer types.

The direct answer to your question is, as one comment mentions, that copies in Go (via variable assignment, etc.) are "shallow copies" in that Go does not dereference pointer types and create new underlying copies. However, since struct type things are not pointer types, it's completely possible for a composite value to be "deep copied".

If you want to see this in action, try modifying your example code so that the ac field on your Copy type is a plain struct rather than a pointer to a struct:

type Copy struct {
    age int
    ac  AnotherCopy
}

You should then see that creating a copy of a Copy and setting the ac.surname field doesn't change the value of the ac.surname field on the original struct.

Austin
  • 2,982
  • 2
  • 28
  • 36
  • 1
    https://pkg.go.dev/go/types#BasicKind . A ptr to struct might exist. `maps, slices, channels, and interfaces are all pointer types.`, as far as i understand, they are values containing pointer to some memory. Often they are called slice header, map header etc https://pkg.go.dev/reflect#SliceHeader . https://research.swtch.com/interfaces `Interface values are represented as a two-word pair giving a pointer to information about the type stored in the interface and a pointer to the associated data.` this whole thing is subtle, i hope i have helped at clarifying –  Dec 12 '21 at 09:19