Go structs are copied on assignment (and other things about Go I'd missed)
Go’s value semantics and the fact that structs are copied on assignment highlight how easily programmers—especially those coming from Python, Java, or other “reference-heavy” languages—can misread what mutations actually affect. Commenters unpack how maps, slices, and pointers in Go are all still passed by value, contrast this with true pass-by-reference in languages like C++ and C#, and debate whether Go’s choices clarify or obscure program behavior and performance. Along the way they recommend core learning resources, note common pitfalls such as range-loop copies and slice aliasing, and argue over whether Go’s lack of immutability and its design trade-offs represent simplicity or missed opportunities.
Go value semantics & common pitfalls
- Many comments reiterate that in Go everything is passed by value (copied), including pointers, slices, maps, and channels; what’s copied is often a small descriptor that points to shared backing data.
- A common gotcha: copying a struct, slice, or map variable copies the descriptor, so mutating through either copy can affect shared underlying data, but reassigning the local variable does not affect the original.
- Range loops can copy elements;
for _, v := range slicemay repeatedly copy large structs, with both correctness and performance implications. - Named return values can be silently left at their zero value (e.g.,
errstayingnil) if not explicitly assigned.
Pass-by-value vs pass-by-reference terminology
- Long debate over whether modern languages meaningfully distinguish “pass-by-value” and “pass-by-reference.”
- Several argue nearly all mainstream languages pass by value (often copying a pointer/reference), and that older “true pass-by-reference” semantics are largely historical.
- Others insist the distinction still matters (e.g., C++/C# reference parameters) and should be taught, especially for performance and expressiveness.
- Python’s model (variables as references to objects, arguments as copies of those references) is used extensively to illustrate confusion.
Slices, maps, and reference-like behavior
- Slices and maps are described as small structs containing pointers; copying them copies the struct, not the underlying storage.
- This leads to “reference-like” behavior that surprises newcomers, especially when appending to slices (which may reallocate and break sharing) versus mutating elements or map entries (which affects shared data).
- Some see this as clear and consistent once understood; others find the hidden sharing and performance characteristics non-obvious and error-prone.
Language design, safety, and ergonomics
- Some praise Go’s explicitness and C-like value semantics as easier to reason about than dynamic languages.
- Others view many of these pitfalls as avoidable language design mistakes and argue alternative languages (e.g., Rust) avoid them.
- There is recurring desire for better immutability and optional types in Go; attempts to retrofit immutability are said to be hard in an established language.
Learning, documentation, and expertise
- Strong recommendations to read the official spec and certain Go books; the spec is praised as clear and accessible.
- A Go “100 mistakes” collection is discussed: some find early items too basic, others defend starting simple and building up.
- Several comments value public admission of knowledge gaps as a healthy senior-engineer trait; dissenting voices argue that senior developers, especially listing Go on a CV, should already know these memory details.