Falling in Love: Making a Case for the Rust programming language


Why I Am Writing This Series

I recently found myself doing sizeable amounts of software engineering technical writing. Though I try not to be a zealot, I find myself gravitating towards the Rust programming language as the vehicle of communication alongside C++. Rust, however, has fast become the tool of choice for several of the newer projects I find myself working on. This is of little surprise. I am, after all an embedded systems engineer by profession and software architect who believes in the right language for the job at hand.

I am developing a tutorial blog series called Rust the Good Parts. The first episode in the series is this very article, and a kind of love letter to Rust. The series will be geared towards showing how idiomatic Rust elegantly implements several safety and program execution concerns in modern software development. This article, Episode 0, is an introduction to the series and an explanation of my theory about the philosophy of Rust and why I believe it is indeed a language for the next fifty years.

If you have tried Rust before and walked away frustrated, you are exactly who this series is for. The borrow checker is not the enemy. The complexity you encountered is real, but it is not arbitrary. Rust does not hide the realities of program execution from you. It wants you to understand these constraints so that the better way to develop software becomes the natural way to develop software. That is the thesis of this article, and it is the philosophy the entire series is built around.

There is also a historical argument to make here. We have been writing software professionally for roughly fifty years. The same classes of errors have surfaced in production time and again, in language after language, across system after system. Rust looked at that fifty-year track record and asked a question the rest of the industry had been too busy to ask: what if we simply refused to let those mistakes compile?

A Personal Taxonomy of Languages

After years across the programming landscape, I have developed a personal classification of the major languages, not as doctrine but as a way of locating each one in the broader story of where the industry has been and where it is going.

Visual Basic was a pragmatic tool for its time that you outgrew as soon as you took the craft seriously. Python is a language I find genuinely difficult to classify it does almost everything, enforces almost nothing, and is somehow both the first choice of beginners and the engine of serious machine learning infrastructure. C is a low-level language masquerading as a high-level one. It gives you thin syntax over raw hardware and leaves the rest to you. C++ is the inverse: a high-level language masquerading as a low-level one, it is raw power, but with which comes overwhelming responsibility. Java redefined object-oriented programming for an entire generation. C# is a programmer’s dream language, the most thoughtfully evolved of the mainstream OOP family. Go is back-to-basics, where structure is king and simplicity is enforced rather than suggested, Well, until recently. Recent generic additions to Go has come with a lot of backlashes from the community alluding to Go departing from its simplistic philosophy [7].

Then, there are the “academic” functional languages. Haskell is elegance defined. F# is production-grade elegance. Scala brought functional programming to the Java virtual machine and largely did it right. These languages gave the industry a vocabulary: algebraic types, immutability by default, composition over inheritance. Most of that vocabulary eventually migrated into mainstream languages, but always as a guest, never as the host.

Pardon me if I do not mention languages like Swift and Kotlin. They are modern languages built for specific platforms. They try to incorporate the best features of language design but then, constrain them to specific domains. This may be good for the domain community they represent, but not so much for the rest of us. Similarly, languages like PHP and JavaScript are very good examples of languages that have been community driven languages and served as extensions to base languages, JavaScript from Java and PHP from C/C++, for web-focused applications.

Rust is something different. Like C and C++ in this taxonomy, Rust is actually a modern general-purpose language masquerading as a systems programming language. And among all of these, Rust is the best attempt at a one size fits all, silver bullet the industry has yet produced. That claim deserves unpacking, which is the purpose of this article.

Forty Years of the Same Mistakes

There is a talk on YouTube titled “Rust: A Language for the Next 40 Years” [1]. That talk, has largely influenced this post, and in my opinion possibly did not do full justice to the topic. Perhaps though that may have been by design, with the intention to plant a seed and let the inner workings of our minds figure out why Rust could possibly justify such a bold claim.

The talk by Carol Nichols has a central theme: Rust attempts to move the industry forward by not repeating mistakes that can be avoided. That single sentence is deceptively powerful.

The case begins with a brief history of the Rail road industry and safety concerns within that industry. The anecdote draws a parallel analogy to C, the foundation of modern programming. The operating systems, runtimes, and databases that the entire software world depends on are mostly written in C and its descendants. Notwithstanding, C has a well-documented list of failure modes that have never gone away since its inception:

  • Use-after-free: a program accesses memory it has already released, reading or writing data that belongs to something else entirely.
  • Double-free: a program releases the same memory twice, corrupting the allocator’s internal state in ways that may not surface until much later.
  • Memory leaks: allocated memory is never released, draining resources silently until the system buckles under load.
  • Buffer overflow: a program writes past the end of an allocation, overwriting adjacent memory with data that was never meant to be there.
  • Null-pointer dereference: a program follows a pointer that holds no valid address, crashing or corrupting state in ways that are notoriously difficult to reproduce.
  • Data races: two threads access the same memory location concurrently, with at least one writing, producing results that depend on which thread happens to run first.

The industry’s response to these problems has been a succession of tools layered on top of C, each addressing one class of error after the fact.

Valgrind instruments a running program to detect memory errors at runtime, catching use-after-free and leaks as they occur in the running program. ASAN (Address Sanitizer) compiles instrumentation directly into the binary, catching out-of-bounds accesses and use-after-free at runtime without the overhead of Valgrind’s full simulation. UBSAN (Undefined Behaviour Sanitizer) detects undefined behaviour, the class of C mistakes that produce results the standard does not define, which means results the programmer cannot predict. IKOS is a static analyser developed by NASA that uses abstract interpretation to prove the absence of certain errors without running the program at all. MISRA C is a coding standard, adopted in the automotive and aerospace industries, that prohibits the most dangerous C constructs and requires manual review of everything that remains. Safe-C and Checked C are language extensions that add spatial memory safety annotations to C, requiring programmers to mark pointer extents explicitly. C++ inherits C’s weaknesses in this area and adds several of its own.

Notice the pattern. Every one of these tools is an afterthought added to the language from the outside, requiring developers to opt in, install, configure, run, and interpret. ASAN finds the bug after you wrote it. MISRA tells you not to write it but has no way to enforce that at compile time. Checked C adds the safety annotations you needed but requires you to annotate every pointer by hand, with no enforcement from the underlying type system.

Rust asks a different question: what if the safety was not a tool you added after writing the code, but a property of the code that the compiler proved before your program ran for the first time?

What Rust Actually Fixes

Rust’s answer to the C failure-mode list is not a collection of external tools. It is a set of rules enforced by the compiler at every build, with no opt-out in safe code.

The ownership model is the foundation. In Rust, every value has exactly one owner at a time. When the owner goes out of scope, the value is dropped and its memory freed automatically. No free is required, no destructor is forgotten, and no garbage collector runs in the background. This eliminates the use-after-free, double-free, and memory-leak classes of errors entirely, at compile time, before a single line of your code runs in production.

The borrowing rules extend this foundation. You may have many readers of a value at the same time, or exactly one writer, but never both simultaneously. A reader is called an immutable reference, and a writer is called a mutable reference. The compiler proves, at compile time, that no two pieces of code violate this rule. The result is that data races become a compile error rather than a runtime mystery that surfaces under load after deployment.

Null is replaced by Option. A value of type Option is either Some(value), holding a real value, or None, holding nothing at all. The compiler requires you to handle both cases before you can access the value inside. A null pointer dereference becomes impossible in safe Rust because there is no null. The absence of a value is encoded in the type, not hidden in an implicit convention.

Out-of-bounds access triggers a controlled panic at runtime rather than silent memory corruption. The program stops at the boundary rather than overwriting adjacent memory and propagating corruption to a part of the program that has no idea what happened.

What the borrow checker is doing, in essence, is enforcing a three-permission model across every value in your program: read access, write access, and ownership. You can hold read access from many places simultaneously. Write access is exclusive. Ownership transfers from one place to another when a value is moved. The compiler tracks which permissions you hold at every point in the program and rejects any code that would violate them. This is not a runtime check. It is a proof done before a single instruction executes.

The category of errors that Rust prevents without any additional tooling is the same category that requires Valgrind, ASAN, UBSAN, IKOS, and MISRA in C projects. In Rust, these classes of errors are not caught. They are made unrepresentable.

There are still errors that occur in Rust programs, of course. But these are benign and unavoidable in the sense that they result from logic mistakes, not from the language allowing the programmer to accidentally corrupt memory or produce a race condition. The language has sorted avoidable from unavoidable at the level of the type system, and it lets you focus your attention on the errors that are genuinely yours to solve.

Rust is For Those Who Care About the Craft

Coming from that place, a place of to manage memory or not to manage memory, Rust is the type of programming language that one falls in and out of love with. This is not a language you can rush to learn or force love upon, and if you did, the borrow checker stops you in your tracks.

However, Rust does have a few features that it brings to the table. My argument for Rust in this article is not just about its elegant features as much as it is about its mesmerising philosophy. I want to make a strong case about who exactly Rust is for and the philosophy of why those who end up using Rust simply fall in love with it.

Here is the short version: my hypothesis is that Rust is for those who really care about their craft. My argument is that Rust is not just for geeks but for those who care enough to be passionate about what they build.

Let us start with the geeks. The Rust book [2] has it that Rust is for those who care about systems programming, also known as low-level programming. Systems programming can be described as anything that lies between programming the bare metal and operating-system level programming. If this is the type of work you have a passion for, Rust has you covered. However, Rust has proven itself to cut across the whole spectrum of application development, incorporating everything above the operating system and into high-level application and distributed system development. This means mobile, desktop, web, and server-side applications. Rust is an ambitious platform that boasts the capability to handle any type of development task you throw at it without sacrificing performance or developer productivity or quality engineering. So, Rust does not demand that you be a geek, only that you care enough to be a little geeky. In many ways this article could also be titled “Rust is not for the faint-hearted.”

Now I really wish Rust were the silver bullet every developer desires. It is not, not by a mile, but I will dare say Rust is the next best thing that comes close to being that silver bullet. In philosophy, at least, it qualifies. The wide range of applications Rust handles without breaking a sweat is the first reason it comes close. While this may seem trivial, one can easily miss how big a deal it is. It is one of Rust’s most distinctive features and one that separates it from the pack. I have urged beginners to learn Go because it has a less steep learning curve than Rust, but I am beginning to reconsider that advice. I have used Go for more professional projects than Rust and so far without major problems. Notwithstanding, I am beginning to lose count of the number of times systems written in Go and other languages have been unable to cope with scaling or enterprise load until the platform was rewritten in Rust [3]. In other words, Rust cares about you because you care about your craft. That phrase is the philosophy I am trying to pass across in this article. Or perhaps I should say, you care that your system is durable?

I sometimes ask myself whether I am passionate about my craft. I like to see myself as an engineer who is more interested in the goal than the means. Should that make me care less about how I arrive at the solution? I think not, especially when I am interested in the quality and durability of the solution. This is the bottom line for the dedicated engineer, and it forces us to take extra care and extra thought about the building blocks of what we produce.

To buttress this point, let me talk a little about engineering failures. In my years as an engineer, I have come to appreciate that failures are part and parcel of the engineering process. I have also slowly realised that there are two types of failures: avoidable failures and unavoidable or benign failures. Another way to frame this is as recoverable and irrecoverable errors, where recoverable also includes failures that were unforeseen when they occurred but can be avoided in the future. One example that amplifies this reasoning is exception handling. Although languages like Rust, Go, and Zig replace exception handling with robust error handling, it was an epiphany to me when I realised a whole class of failures can be prevented by simply handling errors explicitly within a program. It was the difference between writing a program susceptible to failures and crashes at the slightest error, such as a null pointer or a missing file, and writing one that is robust, reliable, and handles edge cases gracefully. In other words: fixing the problems before they happened. It is this single engineering mindset that separates Rust from the pack.

The Right Way Should Be the Natural Way

Picture this scenario. Having worked on a nationwide project on time and on budget, I felt confident my software was solid. Without going into details of a possible software sabotage, D-day had arrived and our team was dispatched to various locations all over the country. Then it happened. Murphy’s Law kicked in and the software stalled. The servers were not receiving the results. After calming myself and not going into panic mode, I went straight to work debugging the live project, found the bug, and fixed it in production. The good news was that I had used version control, and on that day, it was a real lifesaver. Even better news was that the data had been stored locally, so the project was not a complete flop on the first rollout, and it was only at the centre I was allocated that the results were eventually uploaded to the servers. Tools like version control pre-empt and resolve problems before they become catastrophes. Without tools like these, software development would have continued to be insurmountable.

Fixing the bug in production using version control is only half the story. The major lesson I learned that day, the hard way, was that logging is paramount to debug software in production. The failure had happened without any insight into the point of breakage. Had logging been in place, I would have had at least a clue about where the deployment had failed. In recent times I have become paranoid about logging and software observability in every software development project I undertake. It may take a bit more effort to achieve, but it is wildly beneficial in the long run.

How does this story involve Rust? This is what Rust strives for. Rust says: we have been in the business of high-level software for the past fifty years. We have seen many classes of errors, over and again. Did we learn nothing? What can we do differently?

So, back to Rust. The language has taken a good look at the state of languages for the past fifty years and asked itself: what have we learned, what has worked, what has not, and can we come up with something that incorporates the best of everything to create something better, corrected, and elegant? This, I believe, is the philosophy of how Rust cares about the software developer who cares about their craft.

The better. Rust replaces OOP inheritance hierarchies with structure and contracts. Composition is the primary design tool: a type holds other types as fields. Traits describe shared behaviour across types that know nothing about each other. You get the benefits of polymorphism without the fragility of deep inheritance trees.

The fixed. Memory and concurrency errors are eliminated via the borrow checker. Use-after-free, double-free, null pointer dereference, and data races move from runtime disasters to compile-time rejections.

The elegant. Modern and functional development is built into the language. A rich type system gives you generics, closures, iterators, pattern matching, and algebraic types as first-class features rather than library add-ons.

The long-term. Rust editions give the language a mechanism for evolving without breaking existing code. Code written for Rust 2018 compiles in a Rust 2021 project without modification. The rustfix tool automates the migration between editions. The language can move forward without leaving a trail of abandoned codebases behind it.

The avoidable. Fearless concurrency means the same ownership rules that prevent memory errors also prevent data races. The compiler proves that two threads cannot write to the same data simultaneously without synchronisation. This is not a guideline or a convention. It is a guarantee enforced at compile time.

What Rust Brings to the Table

Functional Programming as a First-Class Citizen

Rust integrates functional programming at the type-system level, not as an add-on. The result is that functional idioms are not a style choice in Rust: they are the idiomatic path.

The most immediate evidence is Option and Result<T, E>, Rust’s algebraic types for optional values and fallible operations. Where C uses null and where older languages use exception-based control flow, Rust encodes the possibility of absence or failure directly into the type. A function that might not return a value returns Option. A function that might fail returns Result<T, E>. The compiler requires you to handle both possibilities before you can access the value inside. You cannot accidentally ignore the possibility that a value is missing, because the type will not let you proceed until you have acknowledged and handled it.

Beyond the algebraic types, Rust’s iterator model brings the full functional pipeline: map, filter, fold, flat_map, and dozens of other adapters that chain together lazily and compile down to loops as efficient as anything you would write by hand. Closures are a first-class feature, with the compiler automatically determining whether a closure captures its environment by reference or by value based on how it is used. The trait system provides the equivalent of Haskell’s type classes, making it possible to define shared behaviour across types that live in separate libraries.

Rust did not inherit functional programming from an academic tradition. It took the lessons the functional community had refined over thirty years and made them mandatory where they prevent bugs, and available as an expressive tool where they make code cleaner.

A Mature Ecosystem

A language is only as useful as what you can build with it, and Rust’s crate ecosystem on crates.io [4] is now large enough and mature enough to cover serious production work across every domain.

Tokio is the async runtime that most production Rust networked applications are built on, providing the same concurrency model as Node.js with the performance characteristics of raw C. Serde provides serialisation and deserialisation to every format you are likely to need, generating the conversion code at compile time with zero runtime overhead. Bevy is a game engine built entirely in Rust, demonstrating that the language handles graphics programming and real-time simulation as naturally as it handles systems code. Axum and Actix provide web frameworks that routinely appear at the top of independent benchmarks. For embedded work, the embedded-hal ecosystem provides hardware abstraction layers that make bare-metal Rust portable across microcontroller families.

Rust’s package manager, Cargo, is widely regarded as one of the best dependency management tools in any language. Adding a crate is one line in Cargo.toml. Auditing your entire dependency tree for known vulnerabilities is a single command: cargo audit. The tooling does not fight you. It handles the mechanical work so you can focus on the actual problem.

The E-book That Wants You to Succeed

Developer onboarding is not an afterthought for the Rust community. The official Rust book [2] is freely available online, and it is genuinely good: clear, well-paced, and honest about the moments where Rust will feel difficult. Rust doesn’t even want you to memorise its entire API. It wants you to understand the basics, and the basics are enough to make you productive. The rest will come naturally. Rust is a language where once you have read the book, you can immediately become productive, with no hidden features lurking in obscure API documentation that will wait for you to earn the title of principal software architect before they reveal themselves. A language where the free book gives you most of what you need is a language that wants you to succeed, not to spend years accumulating tribal knowledge before you can do meaningful work. In summary, “Rust cares for those who care about their craft”.

On Unsafe Code

Although the borrow checker guarantees safety, Rust also offers a way to opt out of the safety guarantees. In the safe subset, the compiler guarantees all the properties described throughout this article. In the unsafe subset, those guarantees are relaxed. The five categories of operations permitted only in unsafe code are:

  1. Dereferencing raw pointers.
  2. Calling unsafe functions.
  3. Implementing unsafe traits.
  4. Mutating global variables.
  5. Accessing fields of unions.

This is important to understand clearly. Unsafe Rust exists and is sometimes necessary, particularly when writing the low-level abstractions that safe Rust is built on. The standard library itself uses unsafe code in a small number of places for exactly this reason. But unsafe code is the exception. It is marked explicitly with the unsafe keyword, reviewed carefully, and contained within small, auditable boundaries. Most of the Rust code in production is safe Rust. The language has made the dangerous operations visible and deliberately opt-in rather than invisible and ubiquitous.

Summary

Like C and C++ in my personal taxonomy, Rust is a modern general-purpose language masquerading as a systems programming language.

It has taken the best parts of modern software engineering, idiomatic functional programming above all, and made no compromise on speed or safety. The overhead is learning the mental model of the borrow checker. You may not need to know every path that leads to a memory error. But you do need to understand what the memory errors are and how the borrow checker prevents them. Once you have that understanding under your belt, the borrow checker stops feeling like a critic and starts feeling like a colleague who caught something you missed.

Rust cares about you when you care about your craft. The language was built by people who were tired of writing the same bug twice, tired of running Valgrind after the fact, tired of hoping the reviewer caught the aliasing problem that the compiler had no way to flag. It is a language that looked at fifty years of avoidable mistakes and decided to prevent them by design. If that resonates with you, you are the person Rust was built for.

What Comes Next: Rust the Good Parts

I’m on a mission to put Rust in every home. I consider the Rust programming language to be the future. The go-to technology for most software solutions. In the AI age, we find ourselves, the demand for high quality software has gone a notch upwards. Rust is strategically placed to fulfil this mandate - ensure quality and robustness without sacrificing speed and developer satisfaction and is fast becoming the language of choice when it comes to AI accelerated software production without compromising safety, security and quality.

This article is the first in a series built around a book I am writing called Rust: The Good Parts, an introduction to Rust that makes no assumptions about prior programming experience. It follows the philosophy of this article: that the natural way to write software should be the right way, and that Rust is the first language to make this a compiler guarantee rather than a professional aspiration. This book will take you from zero to Hero in the basics of Rust and strategically place you in the frontline of highly demanded industry skills.

Part 1 takes you from your first line of Rust to a fully interactive Tic-Tac-Toe game running in the terminal. No prior programming experience is required. The concepts are introduced in order, through concrete examples, at the exact point where you need them. By the end of Part 1, you will have built something real and genuinely understood what you built.

Part 2 goes further. Structs, traits, closures, error handling, generics, and a second Tic-Tac-Toe game built in the Bevy game engine. By the end of Part 2, you will write code that is not just correct but idiomatic: the kind of Rust that makes sense to any programmer who reads it six months from now.

The book, inspired by Havard’s CS50, combines hands-on rigour with first principles understanding. Every chapter addresses universal programming best practices, the kind that senior engineers spend careers accumulating. In Rust, those practices are not suggestions. They are enforced by the compiler. Reading this book is not just learning Rust. It is acquiring, in a single pass, the engineering discipline that most of us earned the hard way.

Who is this book for?

If you have never written a program before, start at Chapter 1. Every concept is introduced through a concrete example before it is explained formally.

If you already know another programming language, Python, JavaScript, Java, or Go, you will move quickly through Part 1. Pay attention to the places where Rust differs. That foreignness is the point. The concepts that feel unfamiliar are filling in gaps that other languages left open.

If you have tried Rust before and bounced off the borrow checker: this book addresses it directly in Chapter 4, using a model built around three permissions: read, write, and own. That framing is the clearest explanation of ownership I have found, and every exercise in Chapter 4 is designed to build the intuition gradually. This is later. Come back.