As I understand it, part of the reason it took a while is that the core Go team was generally of the opinion that doing user-facing SIMD APIs the right way was to design a high-level, cross-platform API that would stand the test of time, and that was then punted a few times given its complexity and need to do other things.
Part of what helped the current approach take off was switching to a philosophy of designing a lower-level architecture-dependent API first (the 'simd/archsimd' package), and then later doing a higher-level portable API (the 'simd' package, which is topic of this blog post).
That two-level approach I think also gave some additional freedom for the design and implementation of the friendlier / high-level 'simd' package, including because the lower-level 'simd/archsimd' package is available for people who need or want to drop down.
It's a nice design.
There are pros and cons of course. You don't have 17 different ways to iterate over an array, so that's nice. But you also went 13 years without generics, despite them being one of the most requested features, because the designers didn't want that complexity inside Go.
Overall I think Go is better for this philosophy but there are times where the language is clearly written more for its maintainers than it's users.
Go shipped with generics (aka bounded parametric polymorphism), but only for built-in types: slices, arrays, and maps. That, with subtyping via interfaces, handled most demand for generics. The most common pain point was custom containers.
Go was first released in November 2009. Russ Cox posted "The Generic Dilemma" [1] in December 2009. The comments show the generics debate raging from the earliest days.
As a fun side note, I forgot I posted a comment on that post pointing to Ada's generics. I was in college, and Ada was our intro language.
[1]: https://research.swtch.com/generic
> the designers didn't want that complexity inside Go.
Yes, with some nuance. Go's goal of writing server programs didn't require the type-system complexity and run-time hit of user-defined generics. [2]
> Go was intended as a language for writing server programs [...] Polymorphic programming did not seem essential [...] so was initially left out for simplicity. > > Generics are convenient but they come at a cost in complexity in the type system and run-time. It took a while to develop a design that we believe gives value proportionate to the complexity.
[2]: https://go.dev/doc/faq#beginning_generics
Out of curiosity, I collected all proposals for Go's journey to generics. https://gist.github.com/jschaf/eaa7aff1af14ea7276a18a1b7370d...
The definition I'm giving is the same as the ISRG's definition at MemorySafety.org. It's the thing everybody is talking about when they talk about memory safety.
The claim being made here is "big if true", because it would imply a lot more languages than Go "aren't memory safe", despite decades without memory corruption exploits.
It's remarkably easy to segfault Go applications with data races. Any object with multiple words (so a slice that's an array pointer, a size, and a capacity. Or a fat pointer with the object pointer and the vtable pointer), can be read in an inconsistent state from two threads which can cause out of bounds reads and writes. And this comes up all the time with how heavily the language encourages concurrency.
It's just difficult to actually exploit because of other considerations that practically add a lot of runtime entropy.
The was for a time a vogue for "zero trust networking" and I'm fond of pointing out that the same thing happened there: people would come up with their own axiomatic derivation of what "zero trust" meant, but in reality it was a term of art meaning "non-Google implementations of BeyondCorp".
Terms of art are kryptonite for message board nerds.
And yeah, we tend to use the PLT definitions when we're talking about literal semantics of programming languages. Nothing in this thread mandates a security focused sub-definition.
And even Rob Pike described Go as "not purely memory safe", in a slide that obviously references this exact data race behavior. https://go.dev/talks/2012/splash.slide#49 They considered this a practical tradeoff for simplicity versus the major managed languages defining what happens during data races in a way that doesn't allow you to break memory safety.
So whats your point here? Haskell?
You said that because you assumed Go is memory-safe in all conditions.
> Go is memory-safe
Yes, but only if there's no data race.
Go is not like Java. Java doesn't guarantee no data race, but when it happens, it's still memory-safe.
I fail to see how a racy Java program is more memory safe than a racy Go program?
Go slices are fat pointers to undecorated memory. The slice itself is a 3-tuple of pointer, length, and capacity. If you append to a slice that's already at capacity, the Go runtime will allocate new memory for you and return a new 3-tuple. If you assign that result to a variable that's also being accessed by another goroutine, the latter can observe the slice in an inconsistent state. It can, for example, see the old pointer but with the new length, allowing out-of-bounds access. None of this requires unsafe code.
The same issue applies to string and interface variables, which are also fat pointers.
Shared Go slices are a bad mix in concurrent code. This is a given. But its also not a fair comparison, you should instead compare java arrays to go arrays, not slices.
This goes for slices, strings and maps. Those a usually wrapped in a mutex, or used with sync primitives like sync.Map.
The point is that Java does not have this problem in the language or the standard library. Of course, you should not write racy Go code; the language provides ample ways to avoid the race, such as channels; and the race detector will generally find such racy code, provided you turn it on. However, the issue is that this race leads to memory-safety violations; it can occur especially in code written by novice Go programmers, and it's well acknowledged by the language authors [1].
The same can be said of C or C++ constructs (and many "anti-Rust" people have historically said that) -- the point is that their use is not enforced by the language and so bugs can lead to panics.
I write a fair amount of Go and Rust so I really don't think either language's flaws are fatal, but it comes off as weirdly defensive to redefine memory and data safety to be "well if you use it properly it's safe". It's totally fine to say this is a problem the Go language did not find important enough to require compile time enforcement and so solving it is done by convention and testing with the race detector (which a similar answer C and C++ give to this problem).
> I have never seen real Go code (i.e. not code written purposefully to be exploitable) that was exploitable due to a data race.
And from tptacek in that same discussion [2]:
> The fact is that Go doesn't admit memory corruption vulnerabilities, and the way you know that is the fact that there are practically zero exploits for memory corruption vulnerabilities targeting pure Go programs, despite the popularity of the language.
Race conditions in general are another matter, and aren’t generally considered a requirement (though you can certainly create nasty bugs).
Thread a = new Thread(() -> x++);
Thread b = new Thread(() -> x++);
a.start();
b.start();Go does not have that.
(Of course also Java may suffer from memory safety issues on system boundaries to unsafe code and due to JVM bugs.)
See for example comments from tptacek like:
https://news.ycombinator.com/item?id=43335748
https://news.ycombinator.com/item?id=46028232
https://news.ycombinator.com/item?id=44672371
(The gist: memory safety is a term of art coined by security practitioners. Go, Python, Rust, Java, others: memory safe. C/C++: memory unsafe. Periodically, people in different slices of industry or academia come up with new definitions of memory safety that declare Rust or Go or other languages to be memory unsafe, but that is not by the broadly accepted definition across industry.)
At some point in the future, a fully backwards compatible Rust compiler will report an error when you try to compile cve-rs.
btw, it's not actually that hard to write correct code in C++, easier than in C because you have all the container types. The problem is that nothing will tell you when you write incorrect code - there's no guarantee.
I mean, sure, insofar as such a thing would also imply that a) the standard would need quite a bit of cleanup/clarification work to not contradict your definition, and b) the main optimizing C++ compilers are miscompiling code, analogous to how cve-rs is a rustc miscompilation rather than an issue with Rust itself.
(Fil-C might be an interesting exception here, though IIRC its definition of memory safety is slightly different)
There are no known instances of this bug being encountered in the wild, and if you look into it, you will see how extremely unlikely such code is.
I like Rust, but with this bug existing for so long, I personally no longer think of it as memory-safe.
I suppose it depends on who is doing the classifying? From the developer's standpoint I'd imagine intent is all that matters: a bug is something that does not match developer intent and that is (eventually) expected to be changed to match the intent, while a feature is something that does match developer intent regardless of how old/new it is. From a user's standpoint I'd imagine it's a combination of developer intent and the user's reliance on said behavior, but IIRC in this particular case there's no known non-demo code that has organically run into this particular bug so there's little weight in favor of calling the bug a feature despite the devs' stance.
Also as GP said I think one needs to be careful to distinguish between the compiler and the language. IIRC the devs have known for basically this entire time exactly in what manner the Rust compiler fail to implement the rules of Rust the language, but a general fix has been blocked on long-running projects that have only recently been approaching the finish line [1].
[0]: https://news.ycombinator.com/item?id=40431444
[1]: https://blog.rust-lang.org/2026/08/21/enabling-next-solver-o...
As an (outside go) example, Ocaml (5) promises strong memory safety, but not to be data race free. A data race is not something we can prevent, because its usually not bound by code, but by time and the race-source rarely in source-code.
This means we have data races in http, database inserts etc. The source is usually not a concurrent task in source code-land.
I do think the nitpicking about this is mostly from people that want to say “my favorite language is safer than Go” which stupid and annoying.
https://go.dev/ref/mem#restrictions:~:text=such%20races,corr...
https://www.ralfj.de/blog/2025/07/24/memory-safety.html (I don't agree with everything here, but it's a very thorough explanation)
My point is "races" happen all over. In concurrent code, databases, http and pretty much anywhere where you have some kind of timing, not scoped to a unit.
I think this should make the distinction between data races and race conditions pretty clear.
Not sure why you would be that nitpicky for something so trivial?
That throws a NullReferenceException
When we finally rid ourselves of C and C++ is so larded over with extensions and additions and features that we can finally plausibly say the C subset is just not in use anymore, we can perhaps consider as a community expanding what "memory safe" means, but in the meantime it has some very important meanings and we should not try to augment the term. Memory safety doesn't mean anything like "forcing exhaustiveness into sum type deconstructions" or "never has a race condition" (though it does mean said race condition shouldn't be something that allows you to escape out of an array or forcibly change the type on something in a way the language doesn't normally permit) or any of several other things that may be very nice to have indeed, but are not part of the definition of "memory safe".
Memory safe is a very old concept, and almost everything is memory safe now. But not quite, and as such the term still has use. And also zig for some reason gave it up so it won't be disappearing as soon as I'd like.'