My first encounter with Rust was seeing my new CTO literally nail a rusty gear onto the office wall. This was in 2021, the gear was the size of a plate, and in its center was a capital R. So the first thing I learned about Rust was that it’s not like other programming languages. Those logos appear on t-shirts, and anonymous black backpacks. Nobody nails C++ to the wall (except maybe figuratively, on the internet).
The next thing I learned about Rust was that it’s safe, in all these ways that earlier programming languages weren’t. This caught my attention too, because the language was so raw, and so reflective of the roller coaster we get on with our first hello world!
.
I think most of us have this deep-seated emotional arc in our relationship with programming. We started in software because writing code made things happen – because code is power1. Then at some point between your first line of code and your reading of this essay you learn that power is actually terrifyingly complicated. You break a build. You introduce a bug that takes your team weeks to find. And then you have to deal with how that makes you feel, and by and large you’re dealing with your feelings alone because coding is a solitary activity, and at moments like this it can easily feel like even the computer isn’t on your side.2
So when Rust comes along and offers you a helping hand, it’s remarkable, because so few other languages do. Even if it sometimes feels like that helping hand is dragging you up a wall that’s covered in broken glass, it’s still better than plunging into the shark-filled moat below.
I’m talking, of course, about the compiler, and memory safety, probably the two most distinctive features of the language.
Memory safety issues mean you can’t trust what you’re seeing in your source code anymore. There’s no program analysis you can perform to reason about your code anymore because you’re breaking assumptions that your compiler has about the properties of your program, so there’s a fundamental disagreement between what you see in the code and how the compiler will interpret it.
But these don’t (generally) happen in Rust3, because the compiler checks all your assumptions about memory for you, by doing a static analysis of the code when it compiles. Working with the compiler has been described as “siege warfare,” and not everyone loves having to speak the language of the machine, which sometimes makes you do things like itemize the variables in code you damn well know is safe.
Rust programmers call this “convincing the compiler,”4 and here Rust’s architecture works in our favor. The compiler demands that our code is explicit, and the language provides opportunities to make it so. You can write unsafe code as long as you declare that you’re doing so, and then wrap unsafe code in a safe wrapper. The type system both encourages you to modularize concerns and to declare exactly what functionality you want to expose. Make all the nullables you want, then declare how you’re handling each and every case.
One senior engineer at Antithesis told me that he likes to treat the Rust compiler “as a friend” – one who won’t let him do anything too bad, and from this perspective, you actually don’t have a lot of friends when you’re coding. C++ makes you check your own safety invariants, which is fine when you wrote the entire codebase, and less fine when you’re Microsoft. Javascript will happily run code with type mismatches and nulls, with a well-defined runtime behavior of crashing. That Rust provides such guardrails at all is kind of a novelty.
So I’m inviting you to consider your relationship with this activity on which (I assume) you spend a great deal of your time. Programming might be a mostly rational exercise, but it’s also a thing humans do, and humans are emotional creatures. I think Rust works for people not just because it works (on a technical level), but because it – and I know this phrase will be controversial – makes us feel safe as we build our worlds at the keyboard.
In that light, it’s unsurprising that so much of what’s written about Rust has this sense of “oh my god we finally fixed programming.” We’ve all been cut by the rough edges of our programming language, which are old and jagged and vicious. And like everything else about computing, they’re our responsibility. They’re not physical phenomena. We put them there. And we put them there because safety, flexibility, and performance are an iron triangle, and giving up some safety meant we could build bigger or better.
Remember, the roller coaster has ups as well as downs. There are days when coding is actually fun – and writing code is fun because it makes things happen.
If you’re writing Rust, you’re probably not just doing so because you’re a fan of the language, but because you actually need it for some reason. You’re writing something that needs to be fast, distributed, reliable. Something that could be very very big. Rust can do all that, because in addition to being safe, it’s very, very fast.
We started working with distributed systems for much the same reason – they let us build bigger and better than single systems did.5 But with multiple processors came multiple layers of complexity. Our environments and architectures became much more complex – we started having to worry about network faults, timing faults, and data races.
And here’s the thing about distributed environments. They’re almost inimical to the philosophy that makes Rust safe.
Explicitness is a key part of how Rust builds safety in your code, but there’s no way to give your program an explicit understanding of the production environment. The environment, with all its rough edges, implicitly shapes how your code will run.
The Rust compiler in a sense functions as a test for your program, and most tests, the compiler included, imply perfection in the runtime environment. What else could it do? The machine only knows what it knows. The Rust compiler may be one of the best of its kind, but it isn’t going to check your business logic or the resilience of your program in the face of the kind of faults it will encounter in production. The most important thing to be aware of with any piece of safety equipment is that safety has its limits. You need to know what the compiler can’t catch.
But what if you could know that your code is actually safe in all those ways too? Not just memory-safe but actually reliable? What would you build?
You’ve probably worked on something that you wouldn’t have tackled except in Rust, because the compiler gave you confidence. What would coding feel like if that confidence extended to every dimension of your code?
At Antithesis, we hear from customers about all kinds of projects that they wouldn’t have tackled without the sense of safety we provide – complex, massively multi-threaded systems, rewrites that pay off years of tech debt, building new database systems from scratch – the truth is, safety unleashes ambition. You dream bigger if you know there’s something catching your mistakes.
If you’d like to know more about what it’s like to combine Antithesis with the Shuttle framework, you can try this out today. Or, to see what we just built, using the combination of Rust and Antithesis testing, come see us at the Monsterscale Summit online, this March.
Footnotes
-
If you’re very lucky, maybe you’ve held on to some of that sensation, through the interviews and the meetings and the code reviews and Jira and churn and managers who want to know what you do by the hour. I hope you have. ↩
-
I mean, it isn’t. It’s a machine. But you probably know what I mean. ↩
-
Safe Rust, at least. I know it’s technically possible for this to happen. But it’s rare to see this happen in everyday use, rare enough that Rust’s reputation for memory safety is well deserved. ↩
-
One interesting thing about this is that you’re usually compiling soon after you originally wrote the code, so you’re likely to remember exactly what you were trying to do, even if you don’t know exactly what you did wrong. Context is a wonderful thing. ↩
-
I know this isn’t strictly true, but “the project won’t fit on a single box” is probably the most common reason. ↩