Why Rust?
Every decade or so a new language earns a place at the top of the stack. Rust is making that case right now — and for good reason. This page explains the concrete, technical reasons why developers, companies, and even governments are choosing Rust, and what problems it solves that no other mainstream language addresses as completely.
Memory safety without a garbage collector
The single most important thing Rust offers is a guarantee that does not exist in any other systems language: your program cannot have memory safety bugs if it compiles. No dangling pointers. No use-after-free. No double-frees. No null pointer dereferences. No buffer overflows. These are not prevented at runtime by a safety net — they are rejected at compile time.
To understand why that matters, consider what happens in C when you make a common mistake:
C — use-after-free (compiles fine, crashes or corrupts at runtime)
#include <stdlib.h>
#include <stdio.h>
int main() {
int *p = malloc(sizeof(int));
*p = 42;
free(p); // memory freed here
printf("%d
", *p); // use-after-free: undefined behaviour
// might print 42, might crash, might silently corrupt data
return 0;
}The C compiler emits no warning. The program may appear to work in testing and silently corrupt memory in production. Now look at the equivalent attempt in Rust:
Rust — the same mistake, caught at compile time
fn main() {
let s = String::from("hello");
drop(s); // explicitly drop (free) s
println!("{}", s); // compile error: borrow of moved value
}error[E0382]: borrow of moved value: `s`
--> src/main.rs:5:20
|
2 | let s = String::from("hello");
| - move occurs because `s` has type `String`
3 | drop(s);
| - value moved here
4 |
5 | println!("{}", s);
| ^ value borrowed here after moveThe Rust compiler finds the bug in milliseconds and refuses to produce an executable. There is nothing to debug at runtime because the program never ran.
No dangling pointers
A dangling pointer occurs when you hold a reference to memory that has already been freed. It is one of the most exploited vulnerability classes in C and C++. Rust prevents it entirely through its lifetime system:
Rust — dangling reference caught at compile time
fn dangle() -> &String { // returning a reference to a String
let s = String::from("hi"); // s is created inside the function
&s // we return a reference to s...
} // but s goes out of scope here and is dropped!
fn main() {
let r = dangle(); // r would point to freed memory
}error[E0106]: missing lifetime specifier
--> src/main.rs:1:16
|
1 | fn dangle() -> &String {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value,
but there is no value for it to be borrowed fromThe compiler identifies that the reference would outlive the data it points to and refuses to compile. The fix is to return an owned String instead of a reference — and the compiler tells you exactly that.
Fearless concurrency
Data races are one of the hardest categories of bug to debug — they are non-deterministic, often only appear under load, and can be impossible to reproduce in a debugger. Most languages rely on documentation, code review, and runtime race detectors (like Go's -race flag) to catch them. Rust makes data races a compile-time error.
The rule is simple: you can have many shared read-only references (&T) OR one exclusive mutable reference (&mut T) at a time — never both. This rule, enforced by the borrow checker, is exactly what eliminates data races:
Rust — attempting a data race is a compile error
use std::thread;
fn main() {
let mut data = vec![1, 2, 3];
let handle = thread::spawn(|| {
data.push(4); // attempt to mutate data from another thread
});
data.push(5); // while main thread also mutates it
handle.join().unwrap();
}error[E0502]: cannot borrow `data` as mutable because it is also borrowed as immutable
--> src/main.rs:9:5
|
6 | let handle = thread::spawn(|| {
| -- immutable borrow occurs here
7 | data.push(4);
| ---- first borrow occurs due to use of `data` in closure
...
9 | data.push(5);
| ^^^^ mutable borrow occurs hereThe Rust solution is to use a Mutex or a channel — and the type system forces you to do so correctly. You cannot accidentally share mutable state across threads without proper synchronisation.
Zero-cost abstractions
Rust lets you write high-level, expressive code — iterators, closures, traits, generics — that compiles down to exactly the same machine code you would write by hand in C. This is what "zero-cost abstraction" means: you do not pay at runtime for abstractions you use at the source level.
Consider a simple sum over a vector. Both of these produce identical assembly:
High-level iterator style
let numbers = vec![1, 2, 3, 4, 5]; let sum: i32 = numbers.iter().sum();
Manual loop — identical compiled output
let numbers = vec![1, 2, 3, 4, 5];
let mut sum = 0i32;
for n in &numbers {
sum += n;
}The iterator version is not slower. It compiles to an optimised loop with SIMD instructions on modern CPUs. You get readable, composable code at no performance cost.
Modern, world-class tooling
Rust ships with an integrated toolchain that is consistently rated as one of the best developer experiences in any language:
Cargo — the official build system and package manager.
cargo build,cargo test,cargo run,cargo publish— one tool for everything. Dependency management is reliable and reproducible viaCargo.lock.rustfmt — the official code formatter. Run
cargo fmtand your entire codebase is formatted to a canonical style. No bikeshedding, no configuration debates.Clippy — a linting tool with over 700 lints.
cargo clippycatches common mistakes, suggests idiomatic alternatives, and explains why each suggestion is better.rustdoc — generates beautiful HTML documentation from doc comments. Code examples in doc comments are run as tests with
cargo test, so your examples never get out of sync.rust-analyzer — the official language server. Provides instant type inference, auto-complete, refactoring, and inline error display in VS Code, IntelliJ, Neovim, and every other modern editor.
crates.io — the official package registry. Over 160,000 packages (called crates) are available, covering networking, serialisation, async runtimes, graphics, cryptography, and more.
Rust vs C++ vs Go — a direct comparison
Dimension | Rust | C++ | Go |
|---|---|---|---|
Memory safety | Compile-time guaranteed | Manual (no guarantee) | Runtime GC |
Data races | Compile-time prevented | Runtime (undefined behaviour) | Runtime race detector |
Null safety | No null — uses Option<T> | Null pointers allowed | Nil pointers allowed |
Performance | C-equivalent | C-equivalent | Slightly below C (GC pauses) |
Binary size | Small (no runtime) | Small (no runtime) | Larger (runtime embedded) |
Build tool | Cargo (official, integrated) | CMake / Bazel / Meson (fragmented) | go build (official, integrated) |
Package manager | Cargo / crates.io | vcpkg / Conan / manual | go modules / pkg.go.dev |
Learning curve | Steep (borrow checker) | Very steep | Gentle |
Async/await | Native (library-based runtimes) | Coroutines (C++20, complex) | Goroutines (language-level) |
WebAssembly | First-class target | Supported (complex toolchain) | Limited / experimental |
What Rust prevents — a summary
Use-after-free — accessing memory after it has been freed. In C this causes crashes and exploits. In Rust it is a compile error.
Double-free — freeing the same memory twice, corrupting the allocator. Ownership rules make this structurally impossible.
Dangling pointers — references that outlive the data they point to. Lifetimes prevent this at compile time.
Buffer overflows — writing past the end of an array. Rust bounds-checks all slice accesses by default.
Null pointer dereferences — Rust has no null. Instead it has
Option<T>: eitherSome(value)orNone. The compiler forces you to handle both cases.Data races — simultaneous mutation of shared state from multiple threads. The borrow checker makes this a compile error.
Integer overflow — in debug builds, Rust panics on integer overflow. In release builds you opt in to wrapping arithmetic explicitly.
Uninitialized memory — Rust never allows reading a variable before it is assigned. The compiler enforces definite assignment.
The career case for Rust
Beyond technical merit, Rust is increasingly valuable on a resume. Demand for Rust engineers is growing faster than supply. A few data points:
Rust engineers are consistently among the highest-paid developers in salary surveys — a reflection of supply and demand.
The US government, AWS, Google, Microsoft, and Meta are all actively hiring Rust engineers and building Rust teams.
The Linux kernel accepting Rust guarantees that Rust systems programming skills will be in demand for at least the next decade.
Knowing Rust signals to employers that you understand memory management, concurrency, and low-level systems — skills that are hard to fake.
The Rust community is known for being welcoming, well-documented, and supportive of new learners.
Growing ecosystem
The crates.io ecosystem has grown from near zero in 2015 to over 160,000 published crates. Mature, production-ready libraries now cover:
Async runtimes — Tokio (dominant), async-std, smol.
HTTP servers — Axum, Actix-Web, Warp, Rocket.
HTTP clients — reqwest, ureq.
Serialisation — serde (the most downloaded crate in the ecosystem), bincode, MessagePack.
Databases — sqlx (async, compile-time checked queries), Diesel (ORM), SeaORM.
CLI parsing — clap, argh.
Cryptography — ring, RustCrypto.
Parsing — nom, pest, winnow.
WebAssembly — wasm-bindgen, wasm-pack.
Graphics and GPU — wgpu (WebGPU implementation), Ash (Vulkan bindings).