Skip to content

Introduction to Rust

Rust solves a problem that has plagued systems programming for decades: how to write fast, Memory-safe code without a garbage collector. C and C++ give you control and speed but let you shoot Yourself in the foot with use-after-free, buffer overflows, and data races. Java, Go, and C# give you safety but impose GC pauses and runtime overhead. Rust occupies the intersection: compile-time Memory safety verification with zero runtime cost.

The language was originally developed at Mozilla (starting in 2006, sponsored by the Mozilla Foundation from 2009) for Servo, an experimental browser engine. It has since been adopted by AWS, Cloudflare, Microsoft (Windows kernel), Google (Android), Meta, the Linux kernel, and virtually every large technology company that writes systems software.

Three pillars define Rust:

  1. Memory safety without a garbage collector. Ownership and borrowing are checked at compile time. There is no runtime GC, no reference counting overhead (unless you opt into it with Rc/Arc), and no stop-the-world pauses. The borrow checker enforces that every reference is valid for its entire lifetime, eliminating use-after-free, double-free, and dangling pointer bugs at compile time.

  2. Zero-cost abstractions. High-level constructs (iterators, pattern matching, trait-based dispatch) compile down to the same machine code you would write by hand in C. There is no vtable indirection unless you explicitly use dynamic dispatch via dyn Trait. Monomorphization means generic code is instantiated for each concrete type, giving you the performance of hand-written specialized code.

  3. Fearless concurrency. The type system prevents data races at compile time. Send and Sync are auto-implemented marker traits that the compiler uses to determine whether a type can be safely transferred to or shared between threads. If your code compiles, it is free of data races (modulo unsafe blocks).

Rust compiles to native machine code via LLVM. There is no VM, no interpreter, no JIT.

Source (.rs) → rustc → LLVM IR → LLVM → Native binary

The rustc compiler performs:

  • Lexing and parsing → AST
  • HIR lowering → High-level IR for macro expansion and early analysis
  • MIR lowering → Mid-level IR for borrow checking, drop elaboration, and optimizations
  • LLVM IR generation → Passed to LLVM for backend optimizations and code generation

MIR (Mid-level IR) is the key innovation. It is a control-flow-graph-based representation where the Borrow checker operates. MIR is also used for:

  • Const evaluation
  • Drop elaboration (determining when to call Drop::drop)
  • MIR optimizations (inlining, constant propagation)
  • Runtime type information for Any and downcasting

Rust uses a unified toolchain managed by rustup:

ComponentPurpose
rustupToolchain installer and version manager
rustcThe compiler
cargoBuild system, package manager, test runner, doc generator
rustfmtCode formatter
clippyLint checker (catches common mistakes beyond the compiler)
rust-analyzerLSP implementation for IDE support

Install the toolchain:

Terminal window
curl --proto "=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Manage versions:

Terminal window
rustup install stable
rustup install nightly
rustup default stable
rustup target add x86_64-unknown-linux-musl # cross-compile target

Cargo is the single entry point for all development workflows:

Terminal window
cargo new my_project # create new project
cargo build # compile (debug by default)
cargo build --release # compile with optimizations
cargo test # run all tests
cargo doc # generate documentation
cargo clippy # run lints
cargo fmt # format code
cargo publish # publish to crates.io

A Cargo.toml file is the manifest that defines dependencies, features, profiles, and metadata. A Cargo.lock locks exact dependency versions for reproducible builds.

Rust uses an edition system to make breaking language changes without breaking existing code. Each Crate specifies its edition in Cargo.toml. Editions are not version gates, a Rust 2024 crate can Depend on a Rust 2018 crate and vice versa. The compiler supports all editions simultaneously.

Key changes in Rust 2024:

  • gen keyword reserved for generators (async generators, gen {} blocks)
  • Safe unsafe attributes in extern blocks
  • Tightened lifetime capture rules for closures
  • Changes to unsafe block hygiene
  • unsafe_op_in_unsafe_fn warning becomes a hard error by default
TargetUse Case
Embedded (thumbv7m-none-eabi``cortex-m)Microcontrollers, real-time systems
Linux/Windows/macOS (x86_64``aarch64)Desktop applications, CLIs, servers
WebAssembly (wasm32-unknown-unknown)Browser, edge computing
Bare metal (x86_64-unknown-none)OS kernels, bootloaders, hypervisors
Android/iOSMobile via FFI or native modules
  • Fighting the borrow checker. New Rustaceans often try to write C-style code with aliasing mutable references. The solution is almost always to restructure your data ownership, not to add clone(). Interior mutability types (Cell, RefCell, Mutex) exist precisely for cases where you need shared mutation.
  • Ignoring clippy warnings. Clippy catches real bugs. Run cargo clippy -- -W clippy::all and fix every warning before shipping.
  • Overusing unsafe. If you find yourself wrapping large blocks in unsafe. You are likely working against the language. Re-examine your data structure design.

Rust is a systems programming language that gives you the control of C without the memory bugs. Think of it as a strict librarian: every piece of data has exactly one owner, and when that owner leaves the building, the data is cleaned up automatically. The compiler acts as a safety inspector, catching mistakes before your program ever runs. You write code that runs as fast as C, but the compiler guarantees no null pointer dereferences, no buffer overflows, and no data races. It feels restrictive at first, but once the borrow checker clicks, you realise it is preventing real bugs that would haunt you in production.