Rust is a relatively new programming language that has gained the right to be the most loved programming language on StackOverflow surveys for a few consecutive years. In this codeless introduction to Rust, we will explore what all the fuss is about.
Rust started as an idea after a frustrating experience after a long work day. I can understand why Graydon Hoare (@graydon_pub) opened his laptop and started designing a new programming language that helps people write code that does not mess up our beloved elevators.
Rust is an expressive, modern, safe, and fast programming language that has been gaining popularity among developers in recent years. Rust appeared in the programming scene for the first time in 2010, after the hundreds of steps the author climbed unexpectedly. While Rust shines in Systems Programming, and it often classifies as a Systems Programming language, it is, in reality, a general-purpose programming language used in a wide range of areas such as Web Development or Game Development.
Since its humble start, Rust has evolved into a mature and robust language that is a game changer in the programming world. I can say, without a doubt, that Rust is one of the most important pieces of technology in the last 20 years. Many programming languages have seen the light over the decades, but most were similar to one of the already established mainstream languages. Rust did not invent most of the concepts it builds on, but it merges them in a way that tackles actual problems developers face, in a modern way that lives up to today’s standards.
The world of Systems Programming is plagued with memory-related errors such as buffer overflows, null pointer dereferences, or use-after-free bugs. Horror stories can be found on the internet, relating pain and suffering. Those kinds of errors are among the most annoying and difficult to find and fix.
Rust provides mechanisms to avoid the above errors, which cause crashes, security vulnerabilities, and similar programmers’ nightmares. More importantly, it achieves this at compile time:
- Ownership and Borrowing: One way to avoid two pieces of code incorrectly using references at once is to define an owner and borrowers. There can only be one owner of a value at a time. Borrowers can either take ownership or use a reference, but only one can be mutable simultaneously. This ownership model is the primary pillar of Rust’s memory safety.
- Lifetimes: Rust uses lifetimes to enforce that references exist for as long as they are borrowed.
- Null Safety: There is no such thing as Null in Rust (for good). Instead, Rust uses an Option type to ensure that programs cannot dereference null pointers.
- Safe Wrappers: Rust allows programmers to write unsafe code in a way that is isolated from the rest of the code. The
unsafekeyword helps find suspicious but sometimes necessary code that can cause trouble.
Rust is fast, for real. And it does this without letting you shoot yourself in the foot (looking at you, C and C++). What does this mean?
- Zero-cost abstractions: Rust provides high-level abstractions that compile into efficient machine code. There is no need for cryptic code that only your computer will understand.
- Control over memory allocation: Developers can fine-tune the memory usage in their code, which allows for optimizations that help achieve the desired performance.
- Safe concurrency: The ownership model helps write safe programs at compile time. More on this later.
- Small runtime: Rust brings very little overhead to the table. That is important in environments with constrained resources, such as embedded devices.
Rust allows developers to write concurrent programs aggressively without worrying about race conditions, deadlocks, other rabbit holes, or gray hair makers. In the same way, as per memory safety, Rust enforces correctness at compile time. It achieves this via:
- Threads: Rust provides a threads library that features lightweight abstractions.
- Ownership and Borrowing: It allows multiple threads to access the same data safely, freeing developers up from most synchronization concerns.
- Channels: Threads communicate via an asynchronous channel following a Sender/Receiver pattern.
- Atomic Types: These are wrappers around the basic types (booleans, numbers, etc.) that are safe to use between threads.
The above is a summary of Rust features to write safe concurrent and parallel code without losing too much sleep. The language provides even more features than what we have just described.
Rust enforces Functional Programming aspects such as immutability and explicit mutability at compile time. On top of this, functions are first-class citizens, opening the door for higher-level Functional Programming features.
While Rust is not a pure Functional Programming language, it has some Functional Programming constructs, such as:
- Iterators: Rust provides both sync and async iterators.
- Closures: They are anonymous functions that capture the environment within a particular scope.
- Pattern-Matching: Rust provides powerful pattern-matching capabilities, making it easier to write exhaustive code that handles all the cases required by the matched type.
- Type Classes: Achieved with generic type parameters. Each different parameter in a generic type changes the actual type. For example,
Vec<char>are two different types, not a single type (
Vec) with two parameters.
- Option and Either: Rust includes Option and Either types in its standard library. These types replace the need for Nulls and Exceptions in the language.
Rust’s Functional Programming features help write declarative, instead of imperative, code, helping some developers reason about their code easily.
However, I don’t think Rust should aim to become a fully Functional Programming language. Rust offers a concurrency and parallelism model that does not need immutability, which is one of the holy grails of Functional Programming. In other words: code written in Rust does not necessarily need some of the properties that define Functional code, because Rust has a different way of achieving safety when manipulating shared mutable states.
Rust code can run on different platforms and hardware architectures with barely any modifications. Its tooling can help target and generate platform-specific code when needed:
- Cargo: Rust’s package manager handles dependencies and build targets, downloading and compiling dependencies for different platforms.
- cfg_attr attribute: Specifies the configurations that the code will apply to.
- cfg macro: Helps with conditional compilation by targeting specific platforms.
On top of this, Rust features interoperability with C, which allows for using C libraries that target specific architectures.
Rust offers error and result types instead of Null and exceptions.
Developers can build errors in Rust by implementing the Error trait. The new trait implementations can include information about the error, then extract it with pattern-matching.
Both Result and Error can be used with pattern-matching, helping us ensure that we handle all errors explicitly and that our code is resilient.
Rust also allows propagating errors up the call stack if needed, to help developers handle errors at the appropriate level.
Cargo is the official Rust package manager. It is a modern package manager, similar to NPM, tailored to Rust’s needs to accommodate low-level concerns. In a nutshell, Cargo offers:
- Managing dependencies: Cargo downloads the dependencies in Cargo.toml then compiles the packages.
- Assembling distributables: Cargo bundles the dependencies and the code written by the developer and creates the executables. At this stage, Cargo manages platform-specific code depending on the target.
- Distributing executables: You can upload your crates to crates.io, Rust’s official package registry, and manage their versioning.
- Running tests: It is crucial to have an automated test suite in modern software development. Cargo helps configure and run automated tests.
Compared to languages that lack a C++ dedicated package manager, Rust provides a great developer experience to tame the intricate details that Bare Metal Programming can throw at us.
While you can find unwelcoming people in every community, and Rust is not an exception, I find the Rust community to be overall:
- Knowledgeable: Many Rust developers come from languages like C++ and have extensive experience.
- Helpful: I found people willing to teach me, pair with me for fun, or help out as a volunteer on platforms such as exercism.org. I can only be grateful to them for their time and dedication.
- Passionate: You can feel that people in the Rust community firmly believe in Rust being a ground-breaking technology in the future. I do agree with them.
- Innovative: Rust started fresh not so long ago. Rust developers are trying to push the boundaries of Programming, and look for ways to innovate.
Rust is not an academic language with good intentions and low adoption. Rust is backed by some of the biggest names in the software industry, for a good reason. Here is a non-exhaustive list of 5 real-world projects that use Rust.
The Dropbox crew struggled to refactor and improve their existing Sync Engine Classic, up to the point that only a rewrite would help.
They wrote a new engine called Nucleus. Here is what they say about using Rust to write the new engine:
“[It] was one of the best decisions we made. More than performance, its ergonomics and focus on correctness has helped us tame sync’s complexity. We can encode complex invariants about our system in the type system and have the compiler check them for us.”
Firecraker is the virtualization technology that improves AWS services such as AWS Lambda and AWS Fargate. Firecracker improves efficiency and resource utilization. Rust’s safety features helped Amazon achieve its security and performance standards.
Figma’s Synchronization engine written in TypeScript suffered from latency spikes. This was mainly due to TypeScript running on a single thread.
They replaced the problematic parts of the old engine with a new one written in Rust. They took advantage of Rust’s concurrency and parallelism features, spinning up a new process per document, to improve the server-side performance dramatically.
Coursera used Rust to avoid several security issues that are hard to avoid in C. Rust type system enforces invariants that are otherwise hard to achieve with classic Systems Programming languages like C.
Servo is Mozilla’s parallel browser engine. Servo’s focus is on using multi-core hardware to improve performance. Servo takes advantage of Rust’s safety and concurrency features to build a parallel architecture that delivers high rendering performance.
As we saw in the real-world examples, Rust is a programming language used in critical systems.
Despite being a general-purpose language, Rust is typically used in Systems Programming. From the Linux kernel to embedded devices, or just applications close to the metal, Rust excels in helping developers deliver fast and robust code.
One of the areas where developers have higher hopes is Game Development. While still young, Rust’s Game Development ecosystem is growing and improving. Some studios have adopted Rust in its early Game Development stages.
Game engines such as Bevy are growing and building on the valuable lessons learned from previous attempts to create new Rust Game Development tech. Although I am a hobbyist, I plan to use Rust for Game Development soon.
Another application of Rust is Web Development.
- Rust can help write safe and performant backend code when requirements are tight.
- Web Assembly is where the efforts are to use Rust in the browser. The promise of Web Assembly is secure, almost-native performance. I have worked on some experiments to run Rust code in the browser with Web Assembly. The results were satisfactory despite some limitations around loading and saving files.
I hope that you enjoyed this codeless introduction to Rust. To summarize this article, here is a short wrap-up.
If you liked this article, please check the hands-on followup here.
To summarize, these are the key points where Rust excels:
- Memory safety
Overall, Rust is a powerful language that provides a unique and pragmatic approach to software development, especially in Systems Programming.
Rust is here to stay. I don’t have a crystal ball, and Rust might vanish in a few years. But I highly doubt so. Rust does not look like a temporary fad. It is a great technology that solves problems developers working on low-level projects have suffered for decades. Rust helps tame those problems, and is backed by the support of the big players. Only time will say, but I can’t see Rust going anywhere.
Game Development is, without a doubt, one of the areas where Rust could shine. There is some resistance from developers. For decades, C++ has been the industry standard. Also, Rust is a very terse language, which sometimes does not work well with the iterative nature of Game Development. I can agree with this. At the same time, Rust is incredibly useful for programming game engines, leaving the ever-evolving gameplay logic for more flexible languages such as Lua.
Another field where Rust could be a game changer is the scientific field. If I were to launch a rocket, I would rather trust the launcher code written in Rust than in C or even Python.
The last areas where Rust could be a key player are AI and Machine Learning. A good amount of code is written in C and Python. However, new projects could benefit from Rust’s safety and concurrency features, to create more resilient and performant code. Rust’s interoperability with Python is not as first-class as with C, but this might be worth improving soon. AI is here to stay, so it might as well stay with Rust 😉
Here are a few resources to start learning Rust: