Back in 2019, a security engineer at Microsoft named Galen Hunt wrote a blog post that should've scared every C++ shop on the planet. He explained that roughly 70% of the security vulnerabilities Microsoft patched that year were caused by memory safety issues — bugs that Rust, by design, makes impossible. Five years later, the C++ committee is still arguing about how to "fix" the language, and meanwhile AWS, Google, Microsoft, Discord, Cloudflare, and the Linux kernel maintainers have all quietly started shipping Rust in production. Something fundamental has shifted.
Why This Matters
Let's be honest about why anyone should care about a systems language in 2026. Most developers are building web apps, mobile apps, and microservices. They're not hand-rolling memory allocators. So why does Rust matter to them?
Because Rust is no longer just a "systems language." It's become the language of choice for the infrastructure those web apps depend on. When your Node service talks to a TLS-terminating proxy, there's a good chance that proxy is written in Rust (Envoy's Rust-based components, Cloudflare's Pingora). When you push a container to production, the runc and containerd shims have Rust components. When you query a database, the storage engine in TiKV, Materialize, or DuckDB is Rust. Even the JavaScript toolchain — SWC, Turbopack, Biome, Rspack — is mostly Rust now. Vercel reports that Turbopack's Rust-based core delivers 5-10x faster builds than the legacy webpack implementation.
There's also a cold, hard economic argument. Amazon's Annapurna Labs moved their Nitro system's control plane to Rust and eliminated entire classes of CVEs. Discord famously rewrote their Go-based "Go Live" service in Rust, dropped memory usage from ~10GB to ~50MB, and saw tail latencies fall by an order of magnitude. Dropbox saved a meaningful slice of their CPU bill by porting their sync engine to Rust. None of these teams did it for fun. They did it because the JVM, Go, and C runtimes were either too slow, too memory-hungry, or too crash-prone for their use cases.
And then there's the AI angle. In 2026, every major foundation model company uses Rust somewhere in their inference stack. ONNX Runtime has Rust bindings. Hugging Face's tokenizers library is Rust, and they explicitly chose it because Python's GIL made parallel tokenization painfully slow. If you're building tools for ML engineers, you're probably reaching for Rust.
The Core Idea
So what's actually different about Rust? The honest answer is: it's the first mainstream language that treats memory safety as a compile-time guarantee rather than a runtime convention. C and C++ give you raw pointers and trust you not to mess up. Java and Go use garbage collection to take memory off your plate entirely, but they pay for it with latency spikes, memory overhead, and the inability to reason precisely about where memory lives.
Rust splits the difference. There's no GC, no reference counting, no stop-the-world pauses. Instead, the compiler tracks who owns every piece of memory at any given moment, and the moment two parts of your code try to fight over the same data, the build fails. This sounds restrictive — and it is, at first — but the result is code that's both fast (comparable to C++ for many workloads) and provably free of a whole class of bugs that have plagued software since Unix.
The technical mechanism is the borrow checker. Every value in Rust has a single owner. You can lend out immutable references (&T) to as many readers as you want, or one mutable reference (&mut T), but never both at the same time. The compiler enforces this with the same rigor it enforces type safety. If you've ever had to debug a use-after-free, a double-free, or a data race in a multithreaded C++ program, you can feel the appeal. The bug just... can't compile.
A common misconception is that Rust is "safe but slow." That's wrong. The zero-cost abstractions principle means most Rust code compiles down to roughly the same machine code as the equivalent C++. Inline assembly, SIMD intrinsics, raw pointer manipulation — all there when you need them, fenced behind an unsafe block so reviewers know exactly where to look. The standard library ships with Vec, HashMap, and BTreeMap implementations that match or beat hand-tuned C++ alternatives.
Another misconception is that Rust is only for low-level work. While it shines in kernels, embedded firmware, and game engines, it's also being used to build web backends (Axum, Actix), CLI tools (ripgrep, fd, bat, exa/star), WebAssembly modules, and even desktop apps (Tauri, Druid). The "systems programming" label undersells what the language actually does well.
The cultural piece matters too. Rust has the most active developer community of any language Tracked. The annual survey consistently shows 80%+ of users feel "productive" or "very productive" in Rust, and the language has topped the "most loved" category in Stack Overflow's developer survey for seven years running. That kind of momentum shows up in tooling: cargo is genuinely the best package manager in any ecosystem, cargo clippy catches an absurd number of bugs, and the documentation on docs.rs is uniformly excellent.
A Concrete Example
Let's see what all this looks like in practice. Imagine you're building a high-throughput log ingestion pipeline. You need to parse JSON lines off a socket, validate them, and forward them to Kafka. In most languages, you'd pull in a JSON library, a Kafka client, and some async runtime, and hope for the best. Here's what a minimal version looks like in Rust using Tokio and the rdkafka crate.
use tokio::net::TcpListener;
use tokio::io::{AsyncBufReadExt, BufReader};
use serde::{Deserialize, Serialize};
use rdkafka::producer::{FutureProducer, FutureRecord};
use std::time::Duration;
#[derive(Debug, Deserialize, Serialize)]
struct LogEvent {
timestamp: i64,
level: String,
service: String,
message: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Bind a TCP listener on port 9000
let listener = TcpListener::bind("0.0.0.0:9000").await?;
println!("log-ingest listening on :9000");
// Create a Kafka producer with sensible defaults
let producer: FutureProducer = rdkafka::ClientConfig::new()
.set("bootstrap.servers", "localhost:9092")
.set("message.timeout.ms", "5000")
.set("acks", "all")
.create()?;
loop {
let (socket, addr) = listener.accept().await?;
println!("client connected: {}", addr);
// Clone the producer handle (cheap, it's an Arc internally)
let producer = producer.clone();
// Spawn one Tokio task per connection
tokio::spawn(async move {
let mut lines = BufReader::new(socket).lines();
while let Ok(Some(line)) = lines.next_line().await {
// Parse the JSON; skip malformed lines instead of crashing
let event: Result<LogEvent, _> = serde_json::from_str(&line);
if let Ok(evt) = event {
let payload = serde_json::to_string(&evt).unwrap();
let key = evt.service.clone();
let record = FutureRecord::to("events")
.payload(&payload)
.key(&key);
// Fire-and-forget send with a 5s timeout
if let Err((e, _)) = producer
.send(record, Duration::from_secs(5))
.await
{
eprintln!("kafka send failed: {}", e);
}
}
}
});
}
}
A few things worth pointing out. First, the Result types everywhere force us to think about failure. There's no exception swallowing, no surprise panics. Second, serde_json::from_str returns a typed Result<LogEvent, _>, so once you have a LogEvent, you can't accidentally access a missing field — the deserializer guarantees the shape. Third, the async runtime (Tokio) gives us efficient concurrency with very little memory per task (roughly 200 bytes), so we can handle tens of thousands of connections on a single core.
Compare this to the equivalent Python version, which would need an async framework like aiohttp, would be bound by the GIL for JSON parsing (mitigated by using orjson, but still), and would have memory overhead per connection measured in kilobytes. The Rust version runs on a 50MB container and saturates a gigabit NIC.
Now let's add a unit test to demonstrate how the type system helps:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn log_event_deserializes_correctly() {
let raw = r#"{"timestamp":1700000000,"level":"info","service":"auth","message":"ok"}"#;
let event: LogEvent = serde_json::from_str(raw).unwrap();
assert_eq!(event.level, "info");
assert_eq!(event.service, "auth");
}
#[test]
fn rejects_missing_field() {
let raw = r#"{"timestamp":1700000000,"level":"info","message":"ok"}"#;
let result: Result<LogEvent, _> = serde_json::from_str(raw);
assert!(result.is_err());
}
}
Notice how the second test verifies that omitting the service field fails deserialization. In a dynamically typed language, this would surface as a KeyError three weeks after deploy. In Rust, it's caught the moment you write the type definition.
Common Pitfalls
Fighting the borrow checker instead of restructuring your code. New Rust developers often try to slap
Rc<RefCell<T>>orArc<Mutex<T>>on everything to make the compiler happy. That's almost always the wrong move. If the borrow checker is rejecting your code, treat it as a signal that your data structure wants a redesign. Usually the fix is to split state into smaller pieces with clear ownership, not to add more synchronization primitives.Wrapping everything in
Box<dyn Error>too eagerly. The?operator is great, but if you immediatelyBoxevery error type into a trait object, you lose the ability to pattern-match on specific failures. Define athiserrorenum for your domain errors and reserve trait objects for the very top of your call stack.Overusing
clone()to silence ownership complaints. Yes,.clone()makes the borrow checker shut up. No, you shouldn't do it reflexively. Cloning allocates or copies; in a hot path, that's measurable overhead. Reach for it only when you actually need an owned copy, or useRc/Arcfor shared ownership of read-mostly data.Treating
unwrap()as a strategy. It isn't. It's a panic. Calling.unwrap()on aNoneorErrin production crashes the task. Use it in tests and quick prototypes, but otherwise always handle the failure mode.expect("reason")is marginally better because at least the panic message tells you why.Ignoring
clippylints. Cargo's built-in linter catches an enormous number of performance and correctness issues — usingVecwhere aSmallVecwould be better, blocking on a mutex inside an async function, missing#[must_use]on fallible operations. Runcargo clippy -- -D warningsin CI and treat warnings as errors.
When to Use This (And When Not To)
Rust is the right choice when you're building software that has to be fast, concurrent, and reliable — typically anything in the infrastructure layer. Networking services, databases, embedded firmware, game engines, operating system components, compilers, parsers, and CLI tools all benefit. If you're working on something where every millisecond of tail latency costs money, where a memory leak would page the on-call engineer at 3am, or where you need to safely share state across threads, Rust's guarantees are worth the steeper learning curve.
Rust is also increasingly the right choice for WebAssembly modules that need to run in the browser. Because Rust compiles to tight WASM with minimal runtime overhead, it's the preferred language for performance-sensitive browser libraries — image and video codecs, cryptographic operations, physics simulations, even portions of Figma's rendering engine.
It's probably not the right choice for one-off scripts, throwaway prototypes, or anything where you're optimizing for time-to-first-commit over long-term reliability. Go is still a better fit for many cloud services where raw throughput matters less than developer velocity. Python and TypeScript are still better for data science, glue code, and most business logic. And if you're maintaining a large C++ codebase, the migration cost may not be worth the safety benefits unless you're actively bleeding from memory bugs.
There's also a category of "Rust is overkill" — internal CRUD apps, marketing sites, simple automation scripts. Use Ruby, Python, or Node there. Picking Rust because it's "the future" without a concrete reason is how you end up with a six-month project that could've been a six-week project.
Wrapping Up
Rust has crossed the threshold from "interesting niche language" to "default choice for new systems work." The combination of memory safety without garbage collection, fearless concurrency, zero-cost abstractions, and a thriving tooling ecosystem has made it genuinely compelling for infrastructure that the rest of the software industry depends on. If you haven't tried it, set aside a weekend and build a small CLI tool with cargo new. The borrow checker will frustrate you for the first few hours, then it'll click, and you'll start to see bugs you used to write in other languages.
Your next step: install Rust via rustup, run cargo new hello-rust, and replace one of your existing Python or shell scripts with a small Rust binary. You'll feel the difference the first time you deploy it and watch it use 5MB of RAM instead of 200.
Further Reading
- The Rust Programming Language Book — the canonical, free tutorial maintained by the Rust core team.
- Rust Async Book — deep dive into Tokio, async/await, and pinning.
- Discord's Blog: Why Discord is Switching to Rust — the classic real-world case study.
- Pingora: Cloudflare's Rust-based HTTP Proxy — a production example at massive scale.
- Rust for Linux — ongoing effort to merge Rust into the kernel itself.
Hermes Smith
