4

I am almost sure of that but I want to be 100% sure. Because this seems weird after JS/PHP.

type Vertex struct {
    X, Y float64
}

func (v Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

So is it true that a deep copy (emphasize on this) of v Vertex parameter will be made on every Scale invocation? And therefore, we should use pointer receivers instead of value receivers in order to avoid the deep-copying?

And the same applies to parameters, right? What are other places where the copying happens?

Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89
  • 2
    Yes, the struct will be copied, but that is _not_ what is usually referred to as a "deep copy". This is a simple struct, with no internal pointers. Only the value passed in will be copied. – JimB Jul 20 '19 at 13:47
  • @JimB And if it wasn't a simple struct (but with nested structs) then it would be deep copy, right? – Nurbol Alpysbayev Jul 20 '19 at 13:48
  • *"Does golang deep-copy the whole receiver value?"* No, all you get is a "shallow" copy. – mkopriva Jul 20 '19 at 13:59
  • 1
    @NurbolAlpysbayev: nested struct values would be copied, because it's part of the value. Think about the memory layout of the struct, and what's being passed in. Pointers are not dereferenced, so only the pointer values would be copied. Same goes for maps and slices. – JimB Jul 20 '19 at 14:02
  • @mkopriva I think what you mean is that pointer fields will be copied as pointers, not the values they point to. But value fields will be fully copied. – Nurbol Alpysbayev Jul 20 '19 at 14:08
  • @NurbolAlpysbayev what I meant is what I wrote, "shallow copy", that's it. What *you* mean by "shallow" and "deep" copy, however, is not entirely clear to me but it seems you might be mixing it up a bit. This is also indicated by both answers that had to specifically point out the "depth" of the copy. – mkopriva Jul 20 '19 at 14:39
  • @Mkopriva it's not clear for you probably because you are not too familiar with js or PHP objects. There all objects behave as pointers and so copying object value is impossible (you could traverse the object field by field though to make copying) – Nurbol Alpysbayev Jul 20 '19 at 15:06

2 Answers2

6

Yes, a copy is made. Use pointer receivers if the value is large, you care about avoiding copies, and there are no other considerations (like wanting a copy for various reasons).

The Go FAQ has a useful entry on value vs. pointer receivers.

Note that a copy is made, but you say "deep copy" so be careful here - usually deep copy implies recursive copies including what pointers point to (like copying a whole tree). The copy here is a shallow copy. If Vertex has pointer fields, the values of these pointers will be copied, but not what they point to. There is plenty of discussion of shallow vs. deep copies in Go online; for example this SO question. If there are struct fields, the values of these will also be copied, so it's "deep" in the sense of struct nesting, but not "deep" in the sense of pointers.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • 1
    Wow, Eli Bendersky, I've learnt parsing from your great article on eli.thegreenplace.net (I've read it like 20 times). Thank you! – Nurbol Alpysbayev Jul 20 '19 at 13:39
  • So the same applies to parameters? Eli, I am sorry if I am bothering too much instead of reading the docs (I do but I often miss things), but what are other places where the copying happens? I'll add this to the question. – Nurbol Alpysbayev Jul 20 '19 at 13:48
  • You can think of the receiver as a special kind of parameter here (like `self` in Python). By value copies parameters and receivers – Eli Bendersky Jul 20 '19 at 14:02
4

Does [Go] deep-copy the whole receiver value?


No. Go does not perform a deep copy, only a shallow copy.

In Go, all arguments are passed by value, as if by assignment to the parameter or receiver.


therefore, we should use pointer receivers instead of value receivers.


Not true,

For example, from the Go standard library:

Package time

import "time"

type Time

type Time struct {
  wall uint64
  ext  int64
  loc *Location
}

A Time represents an instant in time with nanosecond precision.

Programs using times should typically store and pass them as values, not pointers. That is, time variables and struct fields should be of type time.Time, not *time.Time.

peterSO
  • 158,998
  • 31
  • 281
  • 276
  • Interesting example with time. Currently I am not fully understanding it, but I am sure soon, after some experimenting with the time package I'll get it, and this example will come in handy. – Nurbol Alpysbayev Jul 20 '19 at 14:16
  • @NurbolAlpysbayev: `time.Time` is a small `struct`. Pointers introduce a level of indirection, a cost. Modern hardware performs many optimizations. – peterSO Jul 20 '19 at 14:21