Because pointers come with some overhead, the natural reaction is to avoid them at all costs and resort to passing struct value copies wherever possible.
I’m probably being pedantic, but pointers don’t really have “overhead”, at least not in the sense that most OOP languages have overhead for objects. A pointer in Go is exactly an address, no more no less. Dereferencing a pointer takes some time, but not necessarily more than the time it would take to pass a copy. The main issue with pointers is that you have to be careful about how you use them or else they will escape the stack resulting in an allocation which is expensive.
Unfortunately, Go doesn’t have very good tooling for flagging these allocations (that I know of, anyway). I would love for someone to build a VS Code extension that highlights allocations or a linter that lets me annotate a line or function with //go:noalloc and then yells loudly if that line/function starts to allocate.
When and why do we pass a struct by pointer rather than copy the struct’s value?
The naive answer to the last question would be to decide based on the concrete situation. Is your struct small, copy its values around. Do you need mutable access to one of its values, use a pointer? Has your struct grown too much, use a pointer. Wait, what do you mean by you copied its value around in a thousand places and now need to refactor them all?
Hard disagree: applying an abstract rule-of-thumb without regard for the concrete situation is naive.
The author counter-poses the “concrete situation” against an understanding of how this code is likely to change, and its relationship to the rest of the project; I insist that these factors are necessary to understanding the concrete situation, and without them your understanding is abstract and one-sided.
If your understanding is abstract and one-sided, you will misunderstand how to apply the rule-of-thumb and what was a useful axiom will become self-defeating and harmful.
Take DRY, for instance: the dogmatic application of DRY leads to premature abstraction, which leads to code that is not fit for purpose. The tendency of code should be to reduce duplication, but you often need to tolerate some amount of it before you discover the correct way to abstract it.
I’m probably being pedantic, but pointers don’t really have “overhead”, at least not in the sense that most OOP languages have overhead for objects. A pointer in Go is exactly an address, no more no less. Dereferencing a pointer takes some time, but not necessarily more than the time it would take to pass a copy. The main issue with pointers is that you have to be careful about how you use them or else they will escape the stack resulting in an allocation which is expensive.
Unfortunately, Go doesn’t have very good tooling for flagging these allocations (that I know of, anyway). I would love for someone to build a VS Code extension that highlights allocations or a linter that lets me annotate a line or function with //go:noalloc and then yells loudly if that line/function starts to allocate.
I’d argue the tooling for Go is decent.
Here’s an example that shows how to get started with basic heap escape analysis https://landontclipp.github.io/blog/2023/07/15/analyzing-go-heap-escapes/
Huh, I’ve Googled for this many times and never come across that. Thanks for sharing!
Hard disagree: applying an abstract rule-of-thumb without regard for the concrete situation is naive.
The author counter-poses the “concrete situation” against an understanding of how this code is likely to change, and its relationship to the rest of the project; I insist that these factors are necessary to understanding the concrete situation, and without them your understanding is abstract and one-sided.
If your understanding is abstract and one-sided, you will misunderstand how to apply the rule-of-thumb and what was a useful axiom will become self-defeating and harmful.
Take DRY, for instance: the dogmatic application of DRY leads to premature abstraction, which leads to code that is not fit for purpose. The tendency of code should be to reduce duplication, but you often need to tolerate some amount of it before you discover the correct way to abstract it.
It feels like Alex Stepanov’s papers on Generic Programming would help the author with the questions they’re struggling with:
http://stepanovpapers.com/gppop.pdf
http://stepanovpapers.com/DeSt98.pdf
And the books:
https://www.fm2gp.com/
http://elementsofprogramming.com/