Hacker News new | ask | show | jobs
by robalni 1613 days ago
I don't know what people mean when they talk about "safe" or "unsafe" code. Doing something like `int a[5]; a[2] = 100;` in C is perfectly safe because there is no bug in that code. The only thing that might be unsafe about that is if you change the code because then you might create a bug. Changing code is always unsafe because you can always create bugs in any language, even Rust.

I don't think "safe" or "unsafe" can be a property of code; it can only be a property of something you do, like changing code. I think that something being "unsafe" means that there is a risk with doing it. Programming is always a risk, even if you write Rust code without using the "unsafe" keyword. You can even have arbitrary code execution bugs in Rust programs without using the "unsafe" keyword; think about bugs like SQL-injections.

All of this doesn't mean than I don't think the checks that the Rust compiler does help. They probably help many people to write less buggy code. I just think it makes no sense to call code "safe" or "unsafe".

4 comments

This is only true if you never pass arrays to functions. Once you have a function that takes in an array and does indexing, it is possible to ask if it ever can access memory it wasn't supposed to without changing that function.
In my opinion it's pretty clear. Rust promises memory safety to me, so all code that is safe IS memory safe. I need to mark a block "unsafe" and have an understanding with the compiler that it can't ensure memory safety here and I am on my own. Makes perfect sense to me :)
If we say that "safe" code is "memory-safe" code, now the question is: what is memory-safe code? If memory-safe code is code that doesn't access the memory in unintended ways, then who knows what is unintended? Only the programmer does; it's impossible to write a compiler that knows what the programmer intends.

Like if you only want to access the data inside the bounds of the array, that's one intention that the compiler will help you to check. If you never intend to access the first element in the array, that's an intention that the compiler doesn't help you to check.

So, there is no memory-safe code or language. I think the only way you could define memory-safe code is that memory-safe code can't contain any code that breaks some rules that the compiler checks for. The problem with that is that those rules could be just about anything, so that definition is pretty useless.

On the contrary, that definition is the whole point and useful if both parties (compiler and programmer) agree what they mean. It's definitely useless for philosophical musings about words and meanings and what not :D
I totally agree; it's useful to have a language feature that enforces some rules that the programmer knows about. It's just useless to call it "safe". My point is just about the words people use when talking about these things :)
To add some more explanation; there are layers of safety. The arrays in Rust or any other language are a layer on top of the memory pages that you get from the operating system. Just like that, you actually have bounds checks in C because the operating system has bounds checks on the memory pages that you use; the safety is just on a lower level.

Languages like Rust add a layer of safety on top of the operating system's layers. The problem is that even if you have safety on one layer, the next layer will always be unsafe, and as long as you have abstractions in your code, you will always have layers.

Let's say you build some kind of abstraction on top of Rust arrays. The compiler will do bounds checks on the arrays but your abstraction will have no checks unless you implement them. Let's say that some state of your abstraction is invalid; the compiler will not help you to check that.

Therefore you can't have a safe language, because even if one layer is perfectly safe, as soon as you add an abstraction layer, you have no safety checks on that layer. SQL injections are an example of that; even if SQL were a perfectly safe language, as soon as you add a layer on top of that (a function that builds SQL code by concatenating strings) you are back to no safety.

https://en.wikipedia.org/wiki/Memory_safety

Outside of your simple example C code, there exists C code which can only be memory safe if the compiler implements a heavy runtime: track pointer allocations, track where pointers source from, raise an error when the pointer is used in an undefined context. See how much work valgrind does to achieve a subset of this task

You could consider C code safe if you included a machine verifiable proof of memory safety with the code.. but that's ridiculously more effort than using Rust

In short, you're arguing semantics over the use of the word safe/unsafe when there's a clear definition Rust offers. You can argue that safe code still has bugs, but that's beside the point

That wiki article seems to define memory-safe code as not containing an arbitrary list of bugs. This doesn't really make sense because even if you have code that doesn't contain those bugs, and even if you have a compiler that helps you to find those bugs, programming is always unsafe. You are not safe just because you don't use the "unsafe" keyword in Rust.
The list isn't arbitrary. Bugs that let you read and write different memory than you meant to are some of the easiest to exploit. If you look back at the type of bugs that make headlines, they're pretty much all memory safety issues or code injection (and the memory issues show up more often).