Rust examples
Every example is a complete program. Read the explanation, then open it in the editor and break it on purpose — that is where the learning happens.
42 examples
Basics
Values, types, control flow and the shape of a Rust program.
Hello, world in Rust
The smallest complete Rust program, why it needs a main function, and how println! actually works.
Ownership: who owns this value?
Rust's core idea explained without jargon: every value has exactly one owner, and moving it hands that ownership away.
Borrowing and references
How to read or modify a value without taking ownership, and the one rule the borrow checker enforces.
Structs and traits
Rust's answer to objects: data in structs, shared behaviour in traits, zero inheritance.
Iterators and closures
Chained, lazy, zero-cost iteration — the most pleasant part of Rust for people coming from other languages.
Error handling with Result and Option
No exceptions and no null: failure is a value you have to deal with, and the ? operator makes that painless.
Fearless concurrency with threads
Real OS threads with compile-time guarantees that you did not accidentally share data unsafely.
Enums and pattern matching
Model the states your program can actually be in, then let the compiler prove you handled all of them.
Variables and mutability
Why `let` is immutable by default, what `mut` really changes, and how the compiler helps you.
Numbers, overflow and casting
Integer and float types, why Rust never converts between them silently, and what happens on overflow.
String and &str
The two text types, when to use each, and how to build, slice and iterate over text safely.
Control flow: if, loop, while, for
Branches and loops in Rust, including `if` as an expression and `break` returning a value.
Functions, expressions and scope
Parameters, return types, the difference between an expression and a statement, and block scope.
Shadowing, const and static
Reusing a name with a new type, and the two kinds of compile-time values Rust gives you.
Tuples and arrays
Two fixed-size types: mixed-type tuples for grouping, single-type arrays for fixed buffers.
Destructuring patterns
Pulling values out of tuples, structs and slices in `let`, in function parameters and in `match`.
Memory
Ownership, borrowing, slices and lifetimes.
Slices: borrowing part of a collection
`&[T]` and `&str` let a function read a range of data without owning or copying any of it.
Lifetimes in functions
What `'a` means, why most functions never need it, and how to annotate the ones that do.
Lifetimes in structs
Storing a reference inside a struct, and what that promises about the owner's data.
Data
Structs, enums, collections and generics.
Vectors: the growable list
Building, reading, iterating and mutating a `Vec<T>` without upsetting the borrow checker.
HashMap and the entry API
Key/value storage, safe lookups, and the idiomatic way to count things.
HashSet: membership and set maths
Storing unique values, testing membership fast, and combining two sets.
impl blocks and associated functions
Methods, constructors, and the difference between `self`, `&self` and `&mut self`.
Derive, Default and struct update syntax
Let the compiler write Debug, Clone, PartialEq and Default for you.
Generics and trait bounds
Write a function once, use it for every type that satisfies the bounds you name.
Implementing Display and From
Give your type user-facing output and painless conversions.
Behaviour
Closures, iterators and traits.
Closures and how they capture
Anonymous functions that remember their environment — and the three ways they can hold it.
Iterator chains that stay fast
map, filter, fold, zip, enumerate and collect — lazy by default, compiled to a tight loop.
Writing your own Iterator
Implement one method, `next`, and inherit the whole adapter library for free.
Sorting and comparing
sort, sort_by, sort_by_key, and what Ord actually asks of a type.
Trait objects and dynamic dispatch
When you need a list of different types that share behaviour, reach for `dyn Trait`.
Robustness
Option, Result, custom errors, panics and tests.
Option and Result in practice
The combinators that replace null checks and try/catch: map, and_then, unwrap_or, ok_or.
Parsing text safely
Turning strings into numbers with `parse`, and dealing with the input that is not a number.
Panic versus recoverable failure
What unwrap and expect really do, and when crashing is the right answer.
Custom error types
An error enum with Display and std::error::Error, so callers can match or just print.
Converting errors with From and ?
One function, several error kinds, still one clean `?` on every line.
Testing basics
#[test], assert_eq!, should_panic — the tests live next to the code they check.
Systems
Smart pointers, modules and threads.
Box and recursive types
A pointer to the heap, and the reason a recursive enum needs one.
Rc and RefCell: shared and interior mutability
When one owner is not enough, and when you need to mutate through a shared reference.
Threads and channels
Spawn work, send results back over a channel, and join before main exits.
Arc and Mutex: shared state across threads
The thread-safe pair for when message passing is not the right shape.
Modules, paths and visibility
How `mod`, `pub` and `use` organise a program, and what Cargo adds around it.