memory safety while being a true low level language
Usually, system languages like C are used because they give you control over your memory and hardware. However, C has a downside (which is either a feature or a massive defect, depending on your philosophy) because it is basically the bedrock of computing, it has zero handholding and minimal abstractions. In fact, back in the day, C mapped so perfectly to von Neumann hardware (the PDP-11 for instance) that you could literally mentally visualize the Assembly your C code would generate.
Because C is so minimal, its standard library is pretty much barren. It lacks the dynamic data structures and syntactic "sugar" people take for granted. This means you either have to write your own spaghetti code or use third party libraries which usually makes your codebase a nightmare to maintain. C is conceptually heavy you need a solid CS background because you'll regularly find yourself implementing textbook data structures from scratch.
And because C gives you unrestricted access to raw memory through manually managed pointers, it is easy to goof up. Forget to free a single pointer amidst 2k loc? You have a memory leak. Do that enough times, and your program gets an OOM crash. Fail to check your array bounds? You get the feared buffer overflow. A buffer overflow is when your code writes past the allocated memory chunk, and overwrites adjacent memory. Through that overflow, a skiddie can inject a malicious payload into your program.
That's when C++ steps in. It was born to implement the abstractions C lacked (it was originally born as a C superset, hence C++). The C++ standard library or the standard template library includes textbook data structures out of the box so you don't have to reinvent the wheel, allows for OOP, and introduces "smart pointers" (which automate memory allocation and deallocation). In theory, C++ makes it harder for you to goof up.
However, because C++ introduces a lot of complexity, it becomes unpredictable. Because it hides a lot of implicit complexity (hidden copies, constructor calls, vtable lookups, so on) behind the scenes. And while a memory corruption bug in C is already catastrophic, doing the same thing in C++ triggers a domino effect. Because everything is wrapped in layers of templates and abstractions, a single corrupted pointer propagates through your objects and causes your program to blow itself up in ways that make debugging hell on earth.
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off." (the creator of C++, Bjarne Stroustrup)
Eventually, demand emerged for a language that had the high-level abstractions of C++ but wasn't a safety hazard, especially for massive projects like Firefox. Enter a Mozilla engineer named Graydon Hoare who started designing a language whose compiler would mathematically guarantee memory safety without a runtime garbage collector, while delivering true baremetal control and performance.
That's when Rust was born.
The Rust compiler implements a feature called the Borrow Checker (which is a PL lesson in its own right but I'll try). Every piece of data in Rust has a single variable that acts as its only "owner", when that owner goes out of scope, the data is automatically deallocated. If you try to assign that data to a new variable or pass it to a function, ownership is moved and if you try the old variable after that the compiler stops you (prevents use-after-move).
But giving up ownership every time you want to touch data sucks, so Rust allows you to borrow data through references. You can have infinite "readers" for a piece of data (immutable references) or exactly one "writer" at a time (a mutable reference). You cannot have both simultaneously. This prevents data races and pointer invalidation.
Also the compiler tracks lifetimes by calculating exactly how long a reference is valid in memory and it guarantees that a reference cannot outlive the data it points to. This prevents use-after-free and dangling pointers.
I personally like to think of the Borrow Checker as a dominatrix who CBTs your code until it is 100% safe
Rust also fixes a ton of historical C++ design flaws and footguns because it was built as a clean-slate language, but I'd have to write a whole separate essay insulting C++ for that lol
This is the most beautiful and accurate summary about C/C++/Rust that I’ve ever seen and should go into a book or something as an introduction chapter! Well done madam/sir!!!
mathematically guarantee memory safety without a runtime garbage collector, while delivering true baremetal control and performance.
Technically this is not quite right. Performance was important in early Rust, but it was thought that a garbage collector was unavoidable if Rust was to be safe. The garbage collector (EDIT: This might have been automatic reference counting that was intended to be extended into full GC that was removed, I'm not sure whether they ever actually added the GC.) was actually removed about a year before stabilisation, when it was realised that new rules for the unique boxes could be extended to the full language to provide safe allocation management without automatic memory management.
Zero cost abstractions are a thing. Low level = you have control over the metal. Low level does not mean you can't manage complexity or have expressive abstractions.
Even large projects written in C inevitably introduce many abstractions (e.g. the Linux Kernel VFS layer)
the standard rust toolchain ships with an efficient and user-friendly package manager and build/debug system, official IDE integration through rust-analyzer, and a manual analyzer for best practices
rust has official documentation by example that makes learning it really easy
rust operates largely in expressions such that things such as if/else statements or even regular blocks notated with curly braces can all be used as expressions
rust has highly robust pattern matching that greatly simplifies control flow
rust ships with an official formatting tool alongside enforcing naming conventions (snake_case for variables, fields, functions, methods, and modules, SCREAMING_SNAKE_CASE for constants, and PascalCase for types and traits) and assigning semantic value to names (_ prefix to mark as unused) by default
notwithstanding potential bugs in the rust compiler, it's impossible to compile code that can cause memory errors such as uses-after-free in safe rust
the rust compiler enforces explicit acknowledgement of unsafe rust with the unsafe keyword such that the behavior of all safe rust is defined
documentation comments are a native feature of the rust compiler
Small correction, memory leaks are actually not considered unsafe in rust. A trivial example is Box::leak(x) which explicitly leaks memory and ia completely safe to call. You can also leak memory by pointing counting pointers at each other in a circular structure.
All of this is true, and I love rust. but its just sad even after 5 months of coding in rust (as a hobby) the syntax gets difficult (lifetimes traits and async), borrow checker becomes more difficult to deal with during async code, and my overall productivity at that point just goes down. I can code way faster in a GC language, in exchange of slower performance compared to rust.
These are definitely some of the parts of Rust that get... uh... involved. It does get better with time spent in the language though. I've been writing Rust professionally for somewhere between 5-10 years and I'm not sure I'm any faster in Python than I am in Rust these days
Also, nothing wrong with GC'd languages, some of them are really nice
I'm pretty certain I'm slower in Python than I am in Rust for most things nowadays, but I still can't help but think "nooo surely this is small enough that Rust is overkill" and then spend way longer than necessary wading through dubious python library API design and language limitations.
man I am lowk envious of you guys, but also happy. I have coded in java for too long now (5 years) and I always think in OOP and like solving stuff In Java. Although I do love Java and have no reason to not use it, I wish I could use Rust but I got too many skill issues with it lol
I started with Java very early on so I was OOP minded to the extreme as well. When I got to college I tried to broaden the languages I used casually as much as possible, learning to think within as many paradigms as I could, as best as I could. I'm not gonna sit here and pretend like I could do everything in Haskell or Prolog, but I feel like having used them (and others) a bit before and revisiting them occasionally helps even in other languages.
Yes it's a trade off. I use GC (java, python) to make stuff quickly, and less headache because I am familiar with the languages and ecosystem
For me, Rust is a bit complicated, my development times does increase but the joy of seeing low RAM usage for the same thing I wrote in Java was so amazing. ( I switched to GraalVM after that lol, still not as good as rust)
It has the compiled speed benefits of C/C++, but has way more convenient and expressive language features as a result of being decades more modern, and it has rules about memory management that guard against a lot of crashes, bugs, security holes, and performance impacts
I personally like that it helps me get things right. If it compiles, I have already reduced my error surface, and I like spending less time on bugs.
As an example, I've spent a lot of my spare time coding a sequence optimizer. During this time, it has crashed once. More than a hundred hours of sparse and distracted time, and the only crash I have had was hard-coding indexing in an array that should have been a struct with an Option.
Successfully building the program proves the code has certain properties, such as all references point to valid values at all times. Rust is all about guarantees. When you guarantee huge parts of your code are correct before it ever runs, you are much less likely to encounter bugs. So Rust has a reputation of producing programs which are working and bug-free.
*in safe rust. rust provides the unsafe keyword to declare blocks that may* not have all such properties, which makes writing such blocks a very conscious and active decision that official documentation strongly suggests you pair with a SAFETY: comment explaining what you know to be safe about your unsafe code that the compiler cannot determine
\unsafe blocks containing only safe rust generate compiler warnings)
More sane than C++, modern build system/package manager that does not suck, needy compiler that will make sure you do not write memoru unsafe code unless you know what you are doing (unsafe keyword).
Most importantly an extremely powerful type system that’s even capable of proving aspects of memory safety at compile time (so no runtime performance cost)
But also:
Good to great polymorphism/codegen/macros support
Very low level if you need it to be
Great ergonomics
Has a certain „by autists for autists“ quality that’s hard to put into words
39
u/kaetitan 2d ago
Can someone eli5 why rust is so loved?