Is the Rust programming language the future of programming?
Programming languages that implement manual memory management (C, C++) give the programmer complete control over what’s happening in memory… but at what cost? Even one small mistake can lead to undefined behavior and security risks.
“Around 70 percent of all the vulnerabilities in Microsoft products addressed through a security update each year are memory safety issues; a Microsoft engineer revealed last week at a security conference.” https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/
Programming languages with a garbage collector (Python, Java, Go, etc.) successfully prevent many memory management errors, but at the expense of performance. These languages are known for being less performant than languages with manual memory management. Garbage collection happens at runtime, meaning that not only your program is running, but so is the garbage collector. This creates a “Stop the World” problem, where garbage collectors halt your program’s execution entirely to go through and manage memory. Because of this, languages utilizing garbage collectors sacrifice performance for memory safety.
Wouldn’t it be nice to have a language that enforces memory safety and performs as well as C does?
Meet the Rust Programming Language.
“The Rust programming language is an open-source systems language that focuses on speed, memory safety, and parallelism.” https://research.mozilla.org/rust/
“High-level ergonomics and low-level control are often at odds in programming language design; Rust challenges that conflict.” https://doc.rust-lang.org/book/
Before diving deeper, I want to specify that this entire blog assumes we’re working with “safe Rust” versus “unsafe Rust” which is used when you want the compiler to look the other way and trust you as a programmer.
Ownership
Rust’s ownership model is a set of rules that the compiler checks at compile-time. If there’s code that doesn’t meet these standards, the compiler will give you a detailed error message. While the code might take longer to compile with all these rules, it doesn’t affect runtime execution and the programmer is confident that the code is safe from many memory bugs.
If you want to know more about the Rust programming language ownership model, check out the official Rust Book. https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
No Null
Null, a built-in constant used to indicate that the expected value is unknown, is a fundamental concept in many programming languages. But null breaks many assumptions you can make about programming languages, which is one reason null pointers generate so many bugs.
“Speaking at a software conference in 2009, Tony Hoare apologized for inventing the null reference: I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.” https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions
Instead of null, Rust has the Option type. The Option type contains Some or None, which forces you to consider what would happen in the absence of data. Null can’t sneak up on you anymore.
Memory Problems & Undefined Behavior
Programming in C requires constantly being on the lookout for memory bugs. That’s not the case when you are using the Rust programming language. When your Rust program compiles you can be confident it is free of memory bugs. The following are a few examples of the types of memory issues Rust manages really well:
Uninitialized Memory
Let’s take a second to talk about stack memory. “The Stack” is a chunk of memory reserved for saving the data we need in a program. Let’s say that we run our program and begin our first function. Each time a function is executed, memory for its data is pushed onto the stack.
Declaring a variable reserves an open spot in stack memory, and initializing a variable writes a value at that memory address. When the function returns, we don’t need its local variables anymore, so the memory of that stack frame can be overwritten.
If we declare a variable on the stack but we don’t initialize a value or write data to the stack, then what’s there? Is it zero? Is it random data? Are we even allowed to access what’s there? Different programming languages handle this problem differently. If accessing this uninitialized memory is allowed, it could be anything. It could be the data from the previous function, previous programs, or just random data. This undefined behavior causes general security concerns.
C allows us to access uninitialized memory. The Rust programming language prevents us accessing uninitialized memory. The compiler will simply throw an error and tell us where to fix the bug.
Null Pointer Dereference
A pointer references and stores a memory address. Dereferencing a pointer refers to retrieving the data stored at the pointer’s location in memory. A null pointer dereference happens when a program tries to dereference a pointer, but instead of the pointer being a valid reference, it’s actually null. This is undefined behavior, which may crash the program. Attackers may intentionally dereference a pointer to try to get around program security.
C, C++, and Java allow null pointer dereferences to occur. The Rust programming language prevents null pointer dereferences, because null simply doesn’t exist in Rust.
Use-After-Free / Dangling Pointers
Use-After-Free (UAF) is a common vulnerability in languages with manual memory management. When memory on the heap is freed, the pointer to that memory becomes invalid and shouldn’t be used again. If the pointer is used again, a UAF vulnerability is created, which can lead to data corruption, program crashes, and general security risks.
Let’s say that two pointers point to the same bunch of memory on the heap, and one pointer frees the memory. The other is left as a dangling pointer, which leads to undefined behavior if it’s deferenced. Dereferencing a dangling pointer may leak unrelated data, cause segfaults, and create hidden bugs.
C allows dangling pointers to be dereferenced. Rust prevents dangling pointers from existing because of its ownership and lifetime model.
Double Free
A double free vulnerability happens when the program calls free() on a pointer that it shouldn’t have access to.
C and C++ allow programmers to double-free pointers. The Rust programming language prevents programmers from double-freeing pointers.
Data Races
Multithreading allows multiple threads to run simultaneously. However, data races in multithreaded code lead to undefined behavior. For example, you have two threads, and they share a variable named x with a value of two. The first thread tries to overwrite x to have a value of five, and the second program tries to overwrite x to have a value of 10. So… does x equal five or ten? It could be either! Data races happen when at least two threads are accessing the same memory, while at least one of the threads is writing to the memory.
C allows data races. Rust prevents data races.
These are just a few examples of how the Rust programming language is great at managing memory bugs
Performance of the Rust Programming Language
Different programming languages will be faster and slower at different tasks because they’re optimized differently. However, benchmarks and testing show patterns in the runtime performance of various languages. Benchmarks show that Rust’s runtime performance in many cases is just as fast as C/C++. Check out these benchmarks for yourself
1 — https://benchmarksgame-team.pages.debian.net/benchmarksgame/index.html
Rust vs. C (gcc compiler)
● Rust is faster in 4/10 of the benchmarks
● C is faster in 2/10 of the benchmarks
● They’re basically identical in 4/10 of the benchmarks (+/- 0.04 secs)
Rust vs. C (Clang compiler)
● Rust is faster in 6/10 of the benchmarks
● C is faster in 1/10 of the benchmarks
● They’re basically identical in 3/10 of the benchmarks (+/- 0.04 secs)
Rust vs. C++ (G++ compiler)
● Rust is faster in 3/10 of the benchmarks
● C++ is faster in 4/10 of the benchmarks
● They’re basically identical in 3/10 of the benchmarks (+/- 0.04 secs)
2 — http://tempesta-tech.com/blog/fast-programming-languages-c-cpp-rust-assembly
Rust vs. C (gcc Compiler)
● Rust is faster in 6/10 of the benchmarks
● C is faster in 2/10 of the benchmarks
● They’re basically identical in 2/10 of the benchmarks (+/- 0.04 secs)
Rust vs. C (Clang Compiler)
● Rust is faster in 6/10 of the benchmarks
● C is faster in 1/10 of the benchmarks
● They’re basically identical in 3/10 of the benchmarks (+/- 0.04 secs)
Rust vs. C++ (G++ Compiler)
● Rust is faster in 3/10 of the benchmarks
● C++ is faster in 5/10 of the benchmarks
● They’re basically identical in 2/10 of the benchmarks (+/- 0.04 secs)
4 — https://julialang.org/benchmarks/
5 — https://pcwalton.github.io/2013/04/18/performance-of-sequential-rust-programs.html
7 — https://coresumo.com/scala-3-0-benchmark-scala-3-0-features-scala-3-0-vs-2-13-1-and-2-13-2-and-2-14/
Conclusion
The Rust programming language is a great way to create memory-safe, high-performance software. With its safety and speed, is Rust the future of programming? Let’s spark some conversation. What do you think?
Kaitlin Berryman is a Software Engineering Intern at Geisel Software, Inc. As a student, she’s diving into learning Rust, C, C++, Python, iOS development, website development, robotics, CNC machinery, and Electrical Engineering. She’s currently attending Rochester Institute of Technology for Electrical Engineering (Robotics Option) and Springboard’s Software Engineering Bootcamp. Her goal at Geisel Software Inc is to learn as much as possible as fast as possible.