Is Rust memory-safe language?

Just like in the title.

Please show your definition of "memory safety". From the previous thread, it looks like it deviates from the one commonly used in Rust community.

You might enjoy reading this post on memory safety.

1 Like

or maybe this post: https://msrc-blog.microsoft.com/2019/07/22/why-rust-for-safe-systems-programming/

and here's a more technical one with specifics: Fearless Concurrency with Rust | Rust Blog

Mini FAQ:

  • Aren't panics like crashes? No, they stop unsafe operations from happening. In an airplane analogy: they are like emergency landing, rather than a crash. Technically, they're closer to a C++ exception.

  • Doesn't unsafe loophole make all safety pointless? No, because unsafe in Rust is used to build safe abstractions on top. For example, String uses unsafe internally to manage memory allocation, but use of String can't cause any unsafety.

Small addition Rust is safe in a way that it doesn't allow you to cause Undefined Behaviour(i.e. you cannot predict what will happen) and you cannot cause potential bugs that are created due to that.

Video that you posted is from guy who has completely different idea of safety.
Panic is not unsafe, it is pretty safe because it guarantees that all destructors(i.e. memory/resources are freed) will be run

1 Like

Technical definitions aside, it certainly feels that way to me when programming. I can write Rust without worrying about memory safety, and in that sense it feels more like a garbage collected language than C where I was constantly scared of messing up when working with pointers.

3 Likes

This is not quite true.

Well, okay it depends on what “guarantees” means. In general, Rust does not “guarantee” destructors to be run in the sense that the destructor would be run no matter what kind of non-unsafe Rust code the program contains. The mechanisms preventing this are

  • memory leaks by reference cycles or explicitly by e.g. std::mem::forget.
  • double exceptions, i.e. exceptions inside destructors that got executed because of a panic
  • there’s also an option to turn every panic into an abort
  • if your program never terminates, some destructors may never be run

But of course, in a weaker sense, a “guarantee” is given, in that the behavior of panic is pretty clearly specified. If you don’t leak memory (which is not that easy to do accidentally) and your destructors never panic, then all destructors are guaranteed to be run before the program terminates. If you don’t leak memory, your program will either run indefinitely, terminate gracefully, terminate due to “panic”, or terminate due to “abort”, and only in the “abort” case you have destructors that aren’t run (but also the abort terminates the program immediately at the moment when the destructor should have run).


I would also recommend to avoid linking the term “safety” too much to destructors being run, since that usually not considered part of what makes Rust a “safe” language.

The term "safe" in Rust, or "memory safe" has a very well defined meaning. Which I summarize as:

  1. It's impossible to inadvertently use uninitialized variables.
  2. It's impossible to inadvertently use a null pointer.
  3. Or us any pointer that does not point to a valid object.
  4. It's impossible to have more than one thread modifying data at the same time. Or a thread reading data while another is modifying it. No race conditions.
  5. One cannot make out of bounds array accesses.

Perhaps I have missed a few cases, others can comment. I would add:

  1. You have to try hard to cause memory leaks. It's possible but does not come naturally.

I'm sure you will agree that all the above rules out a multitude of common bugs we find in C/C++ and other compiled languages. Typically they require hours of messing with debuggers to track down, or luckily now a days we have the sanitizers to help, although they are not totally conclusive.

There is a surprising result of all this. Many people have reported that when they got used to Rust and the "borrow checker" they had learned to think about their problems more as they are forced to organize their code and data around the rules. The feeling is that they end up producing better structured, more organize code. I certainly see that in my code. That is a big bonus.

As an extra safety bonus Rust has a rigorous type system and will detect integer overflow in debug builds.

Yes, one can circumvent all of the above with the use of the "unsafe" keyword. "unsafe" is required to get things done that the compiler cannot reason about, like interfacing to C or actual hardware. Typically "unsafe" is used rarely and applied to small parts of code that one can more easily verify manually. After a year of Rust I have never needed "unsafe"

You seem to have been strongly influenced by the video you linked to in your other thread. Well, I like a good language debate as much as anyone but most of the statements made in that video are easily demonstrably false. Including misrepresenting what Bryan Kantrill said about Rust which I find inexcusable. See the comments under the video. In short that video is just click-bait nonsense.

Rust brings us very valuable features that have not existed any any other language (correct me if I am wrong). For a compiled language to combine memory safety, lack of garbage collector, lack of a complex run-time, provide performance to match C/C++ whilst being as much a high level language as C++ is revolutionary.

6 Likes

Crashes do exactly the same...

So let's add "predictably and reliably every time" to this sentence. arr[x] for invalid x may crash the program, or may corrupt something adjacent in memory.

The predictability is also very important for security, because while a heap-spraying C program may crash every time with random garbage data, a specially-crafted data could exploit that without crashing. Having panics guaranteed rather than opportunistic plugs that vulnerability.

For me the practical difference is that I don't need to use gdb or valgrind with Rust code. Mysterious corruptions just don't happen.

1 Like

wasn’t this “no data races” instead?

2 Likes

Yes, that's correct. Rust only guarantees no data races. Higher-level race conditions (on logical level, or external resources like filesystem, network or external databases) are entirely different problem that Rust can't prevent.

1 Like

Surely this is possible?

In safe Rust out-of-bounds access is not possible, period (it will panic before accessing).

Rust also encourages use of iterators, which eliminate a lot of indexing (instead of for(i=0; i < this better be correct; i++) arr[i] you do for item in arr {item}).

If you use unsafe{} then you can use get_unchecked() and such.

No, they do not.

It's an easy matter to write C/C++ code that uses memory in unsafe ways and perhaps never crashes. It can of course scribble over other data and end up producing wrong results. It can much more easily access data it should not and produce security vulnerabilities.

In a way you are right, if C/C++ would crash immediately when you make a mistake like that it would be great. But it's much more insidious than that. Your memory misuse is likely to let the program run on for some time, producing wrong results in some unrelated part of the code or crashing far away from the location of the actual error. Which is why these memory errors are often very hard to track down and debug in C/C++. Especially in multi-threaded code.

Having C/C++ crash immediately is the best possible outcome. Sadly it is a rare outcome.

1 Like

That actually makes sense. Thanks

Fair enough, I think I get it now. Thanks guys.

2 Likes

Yeah, probably. Sorry sloppy terminology on my part.

I believe that you're right. Thanks for the extensive explanation.

1 Like

Even though it is probably captured by the general rule that “references” in rust always point to valid objects, I like the explicit mention of two more points of memory-safety in particular:

  • no use after free
  • no double free

The logic same applies if you replace the word “free” with “destruct”, or the more Rust-specific term “drop”.

1 Like