I've read this interesting blog post that is totally unrelated to Go and one thing the author said caught my attention, this following quote:
... For example, it is efficient to pass a channel as the parameter of a function all, because a channel in Go is as simple as a pointer to the channel data structure implemented in C. It is the same for map and some other types. But it is inefficient to pass an array or struct; instead, we should pass pointers to these types.
Why is it inefficient to pass pointers when using some of Go internal types or structs?
The context is relevant here:
However, the type system of Go is so complex; programmers need to know all the details about the implementation of standard types, before they can use them correctly. For example, it is efficient ...
The author is saying that maps and channels look like values but act like pointers when they are copied.
For other datatypes, the parameter has a *
in it, which is a clear signal it could be modified in place. Often the argument has a &
in front as well, which is another signal that the argument is being modified.
When passing maps and channels, those syntactic signals are not there. That leads to unexpected results like this one:
http://play.golang.org/p/lS1FXZnxb8
A similar criticism could be applied to the big difference between arrays like [256]byte
and slices like []byte
, where the absence of a size is the only signal of different copying behavior.
All of this being aside, the author incorrectly equates copying and inefficiency. It is true that copying sometimes requires more CPU cycles or memory accesses than passing a pointer. However, that is not always the case. It depends on the size of the structure and optimizations performed by the compiler.
The decision to copy, i.e. pass a value, vs. pass a pointer is also about whether the argument might be modified by the function.
For small structures and arrays which are not intended to be modified by the function, pass them by value. This eliminates an entire class of bugs caused by accidental modifications in place, and is even better than const
as used in other languages, because there is no way to cheat and get around it. Of course, always be careful with embedded pointers, including maps and slices, because those may still be modified inside the function.
It's not inefficient to pass pointers. But the author is correct: because arrays and structs are passed by value by default, their contents are copied to each new function call. This can be inefficient.
According to Go slices: usage and internals, arrays are passed by value. (Slices use a pointer to the array under the hood, so they're more efficient to pass.)
Like slices, channels are allocated by make
(at least implicitly) and so channels act as a reference to the actual data structure.
So when you're working with structs and actual arrays, typically you'll pass their reference instead. (See Sean's answer for more detail about this. He makes a good point that copying isn't always inefficient. Sometimes it is desirable.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With