There is no memory safety without thread safety
Go’s data-race issue in practice
- Several commenters recount hard-to-debug Go races, including one where a loop counter overflow turned some requests from ~100ms into minutes; race detectors didn’t catch it.
- Others say they’ve almost never seen this specific “torn read of fat pointers/slices” in production despite large Go deployments.
- Uber’s study (linked in thread) found
2,000 races across ~50M LoC / ~2,100 services (1 race per 25k LoC), but most did not cause serious outages. - Consensus: the bug is real and nasty when it happens, but empirically infrequent; many teams rely on patterns, tooling, and paranoia rather than language guarantees.
What “memory safety” should mean
- One camp: “memory safety” is binary and means “no UB-style memory corruption in any program written without explicit unsafe,” including in concurrent code. Under that definition Go is not memory safe.
- Another camp (security-oriented): “memory safety” is a term of art meaning “no practically exploitable memory corruption vulnerabilities”; by that standard Go qualifies until someone shows a convincing RCE in real-world Go.
- Long subthread on UB vs unspecified behavior; some argue Go’s data-race-induced type confusion clearly violates typical academic/PL definitions of memory safety, even if exploitation is hard.
- Java, C#, OCaml, Swift ≥6, Rust, etc. are cited as languages whose models ensure races cannot produce invalid pointers or out-of-bounds memory, distinguishing them from Go.
Rust, Go and productivity tradeoffs
- Go is praised for “easy concurrency” (goroutines, channels) and high productivity for web services and glue code; many SaaS backends accept some correctness risk for speed of delivery.
- Rust is viewed as excellent for traditional shared-memory concurrency and eliminating data races, but with higher cognitive load (lifetimes, ownership) and rougher async story.
- Some practitioners claim Rust’s up‑front cost is repaid by far fewer production incidents and lower total cost of ownership; others insist Go (and Python, C# etc.) keep them faster overall, especially once experienced.
- There’s disagreement over whether Rust’s productivity now matches Go’s in practice; internal Google data is mentioned claiming similar team productivity.
Concurrency models and channels
- Despite “share memory by communicating,” real Go programs frequently share memory directly. Channels don’t prevent races without a notion of ownership or sendable types.
- Example given where sending
bytes.Buffer.Bytes()over a channel races withReset()and mutates data being processed; reviewers/tools might catch it, but the language can’t. - Some argue channels + discipline are “concurrency 101”; others reply that subtle, indirect sharing in large codebases still routinely slips through reviews.
Security and exploitability debate
- A published Go data-race based exploit shows type confusion leading to arbitrary code execution, but relies on highly contrived patterns.
- Security-focused commenters demand an exploit against a non‑toy, real Go service before reclassifying Go as “memory-unsafe” in the security sense.
- Others counter that once UB exists, conservative engineering must assume worst-case (potential RCE), even if no public exploit exists yet.
Language ecosystems and evolution
- Swift is in the middle of a painful transition to data‑race freedom; its new concurrency model is seen as safer but complex and tooling-heavy.
- Zig’s and Pony’s claims about safety are questioned; without a Rust‑like ownership model, concurrency may still be unsound.
- There’s meta‑discussion that tool and language “politics” skew these debates; many note most real-world bugs stem from higher‑level logic or database concurrency, not just language memory models.