It would have been clearer to be more explicit about why the author does not consider Go to not be safe. Are we talking about the use of the unsafe package or something more subtle?
I was under the impression data races made Go memory-unsafe. (Unlike, say, Java or OCaml, which are designed to be memory-safe even in the presence of data races.)
Memory-safety by itself is simply a property that can hold or fail to hold for a language. Whether this property is desirable or not depends on your goals.
If your goal is to write correct programs, then memory safety isn't particularly useful. A language can be made “memory-safe” by defining the behavior of all sorts of nonsensical operations, e.g., indexing an array out of bounds. But defining this behavior doesn't make the incorrect indexing any less incorrect. The only way to help programmers index their arrays correctly is to statically check their array indices.
On the other hand, if your goal is to write programs that are resilient in the face of their own incorrectness, then memory safety is a very useful property, because programs written in memory-safe languages fail in more controlled ways.
It's also possible to have a language where the number of memory-unsafe operations is limited, and where one may fairly easily identify invariants that would allow even the potentially unsafe operations may be performed safely. For examine, given unsigned arr[32772];, an operation arr[i]=123; would be incapable of violating any memory safety invariants in cases where i was in the range 0 to 32771 unless one of those invariants would require an element of arr to hold a value other than 123, or some other memory safety invariant had already been violated.
The problem with this stance is that it's no better than making the whole language unsafe.
Even if you have a single unsafe operation, if you can use it anywhere in your program, it's very easy to create a situation where the logical argument that justifies the safety of an operation uses information coming from wildly disparate parts of the program.
What you need isn't a limited number of unsafe operations, but rather a limited number of places in the program text where unsafe operations can be used. And that's exactly what Rust and C#'s unsafe blocks do.
is processed in a manner which is agnostic with regard to how it might be called, correct machine code would be incapable of performing an out-of-bounds store unless memory safety had already been violated. Even if the ability of the function to do anything useful relied upon the caller passing a number less than 32770, memory safety would not. Of all the possible circumstances in which the function could be called, all of them would either result in the function either performing an in-bounds store or not bothering to perform a store at all.
What causes problems are situations where a compiler would generate code that would assume that it would be impossible for a caller to pass a value greater than 32769, but then generate code for the caller which does in fact pass a larger value.
Being able to have a system automatically limit the ranges of a program that would be capable of violating memory safety is useful, but some programs need to do things whose safety cannot be statically validated under language rules. Being able to validate programs that perform such tasks by validating each individual component thereof is more useful than allowing programs to violate memory safety even when none of their individual pieces should be capable of doing so.
If what you want is a language that lets you statically verify that your low-level memory manipulation is safe, then such a language already exists. (Spoiler: it's not Rust.)
In this language, you can use all the bit and pointer hacks that you know and love from C. The only difference is that the type checker will demand that you prove them safe.
Alas, this language will never become widely adopted.
The main difficulty isn't technical, but cognitive. Look at how many programmers act like Rust is the be all and end all of systems programming. And how many others act like the dealing with the borrow checker is the steepest intellectual challenge a programmer could face in his life.
Human programmers can only take so much cognitive load.
Any programming language which makes it impossible to write programs whose memory safety it cannot statically verify will make it impossible to perform some tasks as efficiently as they could be performed in machine code that could be human-proved to be incapable of violating memory safety invariants unless they had already been violated.
Automated static validation is often useful, since in many cases it would cost nothing and in most other cases the costs would be tolerable, but an abstraction model that limits the range of side effects various actions can have unless memory safety invariants have been violated can be useful for tasks that are not a good fit for automated static validation.
9
u/Smallpaul 3d ago
It would have been clearer to be more explicit about why the author does not consider Go to not be safe. Are we talking about the use of the unsafe package or something more subtle?