RustWhy Rust?

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)

C
#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

RUST
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 move

The 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.

The garbage collector trade-off
Languages like Java, Go, and Python achieve memory safety through a garbage collector — a runtime thread that periodically scans memory and frees objects that are no longer reachable. This works, but it costs: unpredictable pause times (GC pauses), higher baseline memory usage, and a significant runtime that must ship with every program. Rust achieves the same safety with zero runtime overhead by doing the analysis at compile time instead.
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

RUST
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 from

The 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

RUST
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 here

The 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

RUST
let numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers.iter().sum();

Manual loop — identical compiled output

RUST
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.

Tip
Bjarne Stroustrup coined the "zero-overhead principle" for C++: "What you don't use, you don't pay for. And further: What you do use, you couldn't hand code any better." Rust applies this principle rigorously and verifiably.
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 via Cargo.lock.

  • rustfmt — the official code formatter. Run cargo fmt and your entire codebase is formatted to a canonical style. No bikeshedding, no configuration debates.

  • Clippy — a linting tool with over 700 lints. cargo clippy catches 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

Warning
Rust has a genuinely steep learning curve. The borrow checker enforces rules that are unfamiliar to developers coming from any other language. Plan for 2–4 weeks before the concepts click and you feel productive. The payoff — code you can trust — is worth every hour of that investment.
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&lt;T&gt;: either Some(value) or None. 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).

Success
If you are learning a systems language in 2024 and beyond, Rust is the most future-proof choice. It is the only language that gives you C-level performance while also being memory-safe, concurrent, and equipped with a modern toolchain. The question is not whether Rust is worth learning — it is how soon you want to reap the benefits.