Introduction to Rust

What Is Rust

Rust is a general-purpose, compiled programming language designed for writing fast and reliable software. It gives the control over memory and hardware that C and C++ give, and it prevents whole classes of bugs, such as use-after-free, dangling pointers, and data races, at compile time.

Rust has no garbage collector and no large runtime. Source code is compiled ahead of time into a native executable.

Core Characteristics

Characteristic Meaning
Compiled Source code becomes a native machine-code binary before it runs
Statically typed Every value has a type known at compile time
Memory safe Safe code cannot read freed memory, use invalid pointers, or overflow buffers
No garbage collector Memory is freed deterministically when its owner goes out of scope
Zero-cost abstractions High-level features such as iterators and generics compile down to code as fast as hand-written low-level code
Fearless concurrency The compiler rejects data races before the program runs
Multi-paradigm Supports imperative, functional, and trait-based (object-like) styles
Expression-based Most constructs, such as if, match, and blocks, produce a value

The Three Goals

Rust is built around three goals that older systems languages usually trade against each other.

            Safety
              /\
             /  \
            /    \
           /      \
          /  Rust  \
         /          \
        /____________\
   Speed          Concurrency
  • Safety: invalid memory access is a compile-time error, not a runtime crash.
  • Speed: performance is comparable to C and C++.
  • Concurrency: shared mutable state across threads is checked by the compiler.

How Rust Achieves Safety Without a Garbage Collector

Rust uses a system of ownership, borrowing, and lifetimes. The compiler checks these rules and then removes them from the final program, so they cost nothing at runtime.

The three rules of ownership:

  1. Every value has exactly one owner.
  2. There can be only one owner at a time.
  3. When the owner goes out of scope, the value is dropped (freed).
let s1 = String::from("hello");     let s2 = s1;

Before the move                     After the move

s1                                  s1  (no longer valid)
+----------+                        +----------+
| ptr  ----+--> "hello" (heap)      | moved    |
| len = 5  |                        +----------+
| cap = 5  |                        s2
+----------+                        +----------+
                                    | ptr  ----+--> "hello" (heap)
                                    | len = 5  |
                                    | cap = 5  |
                                    +----------+

Using a value after it has been moved is rejected by the compiler:

// ❌ does not compile
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;
    println!("{s1}");
    println!("{s2}");
}
error[E0382]: borrow of moved value: `s1`
 --> main.rs:4:16
  |
2 |     let s1 = String::from("hello");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |     println!("{s1}");
  |                ^^ value borrowed here after move

Borrowing lets code use a value through a reference without taking ownership. A value cannot be modified while a reference to it is alive:

// ❌ does not compile
fn main() {
    let mut v = vec![1, 2, 3];
    let first = &v[0];
    v.push(4);
    println!("{first}");
}
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
 --> main.rs:4:5
  |
3 |     let first = &v[0];
  |                  - immutable borrow occurs here
4 |     v.push(4);
  |     ^^^^^^^^^ mutable borrow occurs here
5 |     println!("{first}");
  |                ----- immutable borrow later used here

v.push(4) may move the vector's data to a new heap allocation, which would leave first pointing at freed memory. Rust stops this at compile time.

What Rust Code Looks Like

fn main() {
    let name = "Rust";
    let year = 2015;
    println!("{name} 1.0 was released in {year}.");
}
Rust 1.0 was released in 2015.

Rust has high-level features such as closures and iterators:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let sum: i32 = numbers.iter().sum();
    let evens: Vec<i32> = numbers.iter().copied().filter(|n| n % 2 == 0).collect();
    println!("sum = {sum}");
    println!("evens = {evens:?}");
}
sum = 15
evens = [2, 4]

This compiles to machine code comparable to a hand-written loop.

Safe Rust and Unsafe Rust

Rust has two modes:

Mode Description
Safe Rust The default. The compiler guarantees memory safety and freedom from data races
Unsafe Rust Opt-in with the unsafe keyword. Allows raw pointer dereferencing, calling foreign functions, and a few other operations. The programmer is responsible for upholding safety

unsafe does not turn off the compiler's checks. It unlocks a small set of extra operations. Most Rust programs contain little or no unsafe code, and the standard library wraps unsafe operations in safe interfaces.

The Rust Toolchain

Rust comes with a standard set of tools:

Tool Purpose
rustc The compiler
cargo Build system, package manager, and test runner
rustup Installs and manages Rust versions and toolchains
rustfmt Automatic code formatter
clippy Linter that suggests idiomatic improvements
rustdoc Generates HTML documentation from code comments
rust-analyzer Language server for editor support
crates.io The public registry of Rust libraries, called crates

Where Rust Is Used

Area Examples
Systems programming Operating system components, drivers, embedded firmware
Command-line tools ripgrep, bat, fd
Web backends and services HTTP servers, APIs, microservices
WebAssembly Fast code running in the browser
Networking and infrastructure Proxies, databases, cloud tooling
Game development Engines and game logic
Embedded systems Microcontrollers with no operating system

Language Facts

  • Rust 1.0, the first stable release, came out on May 15, 2015.
  • The language is open source and developed in the open.
  • Rust releases a new stable version every six weeks.
  • Backward compatibility is kept for stable code. Breaking changes are introduced only through editions: 2015, 2018, 2021, and 2024.
  • The compiler is itself written in Rust.