warming up your workspace

Systems Programming in Rust

Learn Rust through typed functions, ownership and borrowing, structs, enums, explicit error values, collections, traits, iterators and concurrency. Practice safe resource access and clear contracts, then compose a checked calculator and a persistent command-driven store. Native threading and optimization have costs and limits; correctness comes before performance claims.

11 projects, 275 hands-on levels, run in your browser.

Syllabus

  • Foundations: code from zero in Rust: Never written code before? Start here. You will learn the basics of Rust, functions, variables, types, decisions, loops, and slices. By the end you are ready for Project 1.
  • Rust Foundations: Rust looks familiar but is strict in ways that pay off later: values are immutable unless you say `mut`, every binding has a known type, and almost everything is an expression that produces a value. This project builds fluency with that core, bindings and shadowing, the integer and float types and their exact rules, booleans and chars, functions and expression blocks, control flow that returns values, and the compound types (tuples, arrays, slices), the ground every later project stands on.
  • Ownership & Borrowing: Passing a String or Vec by value moves ownership; Copy types behave differently. Borrow through references to keep ownership with the caller, use exclusive references for changes, and clone only when a separate owned value is needed. Practice these distinctions with text, vectors, slices and explicit reference lifetimes.
  • Structs & Methods: A struct names a bundle of related fields, and an `impl` block attaches behavior to it. This project builds the object-like core of Rust without inheritance: defining structs and constructing them, associated functions like `new`, methods that borrow `&self` to read or `&mut self` to change, the derive macros that grant `Clone` and equality for free, and composition, structs built from other structs, which is how real Rust models a domain.
  • Enums & Pattern Matching: An enum is a type whose value is exactly one of several named variants, and each variant can carry its own data. This is how Rust models choice precisely: a shape is a circle OR a rectangle, a result is success OR failure. `Option` and `Result` are just enums in the standard library. `match` forces you to handle every case, so whole classes of bug vanish, and `if let` / `while let` give you the concise version. This project builds fluency with enums, Option, Result, and the full power of pattern matching.
  • Error Handling: Represent absence with Option and recoverable failure with Result. Practice transformations, compatible ? propagation, named error variants and input validation. These types make failure visible; callers still choose whether to recover, propagate, panic or deliberately discard a value. Finish by composing integer parsing and overflow-resistant mean calculation.
  • Collections & Strings: The standard library's collections do the heavy lifting in real Rust. This project builds fluency with the three you reach for daily: the growable `Vec`, the owned `String` and its borrowed `&str` view, and the `HashMap` (plus `HashSet`) for keyed lookups and membership. You will build vectors, manipulate text, count word frequencies, sort and dedup, and combine sets, the bread-and-butter data wrangling that every program needs.
  • Traits & Generics: Define shared behavior with traits, reuse it through defaults, and write generic functions with explicit bounds. Practice Display, From, Iterator and Ord, then combine different concrete types through trait objects. Static generics enable specialization; dynamic dispatch provides flexibility. Neither promises a particular execution speed.
  • Iterators & Closures: Closures capture context; iterators describe traversal and transformations one item at a time. Build map/filter pipelines, consume them with folds, combine them with zip and enumerate, and track state with scan. These abstractions can optimize well, but performance depends on the workload and compiler; this project verifies behavior, not speed.
  • Fearless Concurrency: Use threads, move captures, joins, channels and Arc > to coordinate owned data. Safe Rust combines borrowing with Send and Sync to prevent data races when unsafe dependencies uphold their contracts. You still must handle worker failures, close channels and avoid deadlocks or logical races. Compose small parallel patterns without assuming speedup or a particular schedule.
  • Capstone: Calculator & Store Engine: Compose a checked reverse-Polish calculator with an in-memory key-value store. Tokens and commands are enums; Store owns a HashMap; Result and Option express errors and missing values. A persistent Engine runs SET, GET, INCR, DEL and numeric PRINT lines, while run starts a fresh engine for each program. This interpreter combines parsing, arithmetic, state and error recovery; it does not claim to integrate every trait or concurrency exercise.

Key concepts

  • ? operator: In a compatible return context, ? extracts Ok or Some and returns early on Err or None. Result propagation can convert the error using From. It does not automa…
  • Arc: Arc provides atomically reference-counted shared ownership of one allocation. Cloning an Arc clones a handle, not the inner T. Cross-thread use depends on T&#3…
  • Array: A fixed-length, same-type sequence [T; N] whose length is known at compile time. For a growable sequence use a Vec.
  • Associated function: A function associated with a type or trait. Methods have a self receiver; associated functions without a receiver, such as a conventional new constructor, are…
  • Borrow checker: The compiler analysis that checks reference validity and conflicting access: shared reads or exclusive mutable access to the same ordinary data, with reference…
  • Borrowing: Accessing a value through a reference without taking ownership, so the owner keeps the value afterward. The borrow checker enforces the rules at compile time.
  • Box: Box<T> is the simplest smart pointer: it stores a value on the heap and owns it. Used for trait objects, recursive types, and moving large values cheaply.
  • Cargo: Rust's build tool and package manager: it compiles your crate, fetches dependencies from crates.io, runs tests, and manages the project, all from a Cargo.t…
  • Casting (as): The as operator performs explicit numeric casts. Integer narrowing keeps low bits. A finite float-to-integer cast truncates toward zero and saturates at the in…
  • Channel (mpsc): A typed pipe between threads: a sender send s values and a receiver recv s them. mpsc means multi-producer, single-consumer; cloning the sender lets several th…
  • Clone: An explicit duplication via .clone(). String clones own independent text buffers; Vec clones each element using its Clone implementation. Cloning an Arc create…
  • Closure: An anonymous callable, such as |x| x + offset, that can capture surrounding variables by borrow or by value. move selects by-value capture; Copy values are cop…
  • collect: A consuming method that runs an iterator and gathers its items into a collection, the target type chosen by annotation: a Vec , a String , a HashMap , or even…
  • Combinator: A method that transforms or resolves Option or Result, such as map, and_then, filter, ok_or or unwrap_or. Some return another wrapper; others extract a success…
  • Copy: A trait permitting implicit duplication on assignment or argument passing, so the original remains usable. Integers, bools and chars implement Copy; String and…
  • Crate: A Rust compilation unit, either a binary or a library. A Cargo package can contain one library crate and multiple binary crates, with its configuration in Carg…
  • Default method: A trait method with a body, built on the trait's required methods. Implementing types get it for free, or override it. The classic example is is_empty defi…
  • Dereference (*): The * operator reads or writes the value a reference points to. You dereference a &mut i64 to assign through it.
  • derive: An attribute such as #[derive(Clone, PartialEq)] generates trait implementations. Fields and generic parameters must satisfy the generated bounds. Deriving Clo…
  • Destructuring: Pulling a compound value apart into its pieces with a pattern, as in let (a, b) = pair; or matching an enum variant's fields.
  • Drop: Cleanup normally runs when an initialized owned value leaves scope or is explicitly passed to drop. The Drop trait customizes cleanup, and fields are also drop…
  • Dynamic dispatch: Choosing which method implementation to call at runtime via a trait object, as opposed to the static dispatch of generics. It trades a small lookup cost for fl…
  • Entry API: map.entry(key).or_insert(default) looks up a key, inserting a default if absent, and returns a mutable reference, so *map.entry(k).or_insert(0) += 1 counts in…
  • Enum: A type whose value is exactly one of several named variants. Unlike enums in many languages, each variant can carry its own data, making it a sum type. Option…
  • Error handling: Recoverable failure is commonly an explicit Result<T,E>; Option represents absence without a reason. Callers may recover, propagate or deliberately disca…
  • Exhaustiveness: A match must cover every possible case. Explicit variant arms make omissions visible; a wildcard can cover remaining cases. Adding an enum variant breaks match…
  • Expression-oriented: Blocks, if, match and loop are expressions. A block tail expression without a semicolon supplies its value; a function can return that value or return early wi…
  • Fearless concurrency: Safe Rust combines ownership, borrowing, Send and Sync to prevent data races, assuming unsafe code and foreign interfaces uphold their contracts. Programs can…
  • Fn / FnMut / FnOnce: Fn can be called through a shared receiver, FnMut through a mutable receiver, and FnOnce by consuming the callable. Fn implementations also implement FnMut and…
  • Generic: Code parameterized over types, with trait bounds specifying available operations. Rust generally specializes generic code for concrete types through monomorphi…
  • HashMap: HashMap<K,V> owns key-value entries. get returns an Option containing a borrowed value. entry locates an occupied or vacant slot; or_insert inserts a def…
  • HashSet: A hash-based collection of unique values for membership and set operations. Typical lookups have expected constant-time table behavior, but hashing, equality a…
  • if let / while let: Concise forms of match for a single pattern: if let Some(x) = opt { ... } runs only on a match, and while let Some(x) = v.pop() repeats as long as the pattern…
  • impl block: An impl block attaches associated items to a type. Methods are associated functions with a self receiver; functions such as new commonly have no receiver. impl…
  • Integer overflow: Runtime integer overflow panics when overflow checks are enabled, as in ordinary debug builds; behavior depends on compiler settings. Constant overflow may be…
  • Iterator: A lazy sequence produced by next() . Adapter methods (map, filter, zip, enumerate) build pipelines; consuming methods (sum, collect, fold) drive them. Implemen…
  • Iterator adapter: A method returning an iterator that transforms traversal, such as map, filter, take, zip or scan. Per-element work happens when next is requested, directly or…
  • Lazy evaluation: A lazy iterator pipeline delays per-element processing until values are requested. collect, sum, a for loop and direct next calls can all drive it. Setup expre…
  • let binding: let introduces a variable. Bindings are immutable by default; add mut to allow reassignment.
  • Lifetime: The span during which a reference is valid. Lifetime annotations like <'a> tell the compiler how the lifetimes of inputs and outputs relate, so it ca…
  • Macro: Compile-time code generation. Function-like macros use !, as in println! and vec!, and can accept custom syntax or a variable number of arguments. Derive and a…
  • match: Pattern-matching control flow that chooses an arm based on a value's shape and binds its inner data. The compiler requires it to be exhaustive, so every ca…
  • Method: A function inside an impl whose first parameter is self (or &self to borrow, &mut self to mutate). Called with a dot: value.method() .
  • Module: A namespace within a crate, declared with mod , that groups and controls the visibility of items. use brings a path into scope; pub makes an item visible outsi…
  • Monomorphization: Compilation of generic code into instances specialized for concrete types. It enables static dispatch and optimization. It does not guarantee a particular spee…
  • Move: Assigning or passing an owned value (like a String or Vec) transfers ownership to the new binding. The compiler then forbids using the old name, ensuring a sin…
  • mut: let mut permits reassignment or mutation through an owned binding. &mut T is an exclusive reference that allows changing its referent; the reference bindin…
  • Mutable reference (&mut T): An exclusive borrow, written &mut T, permits changing the borrowed data. Conflicting accesses to the same data cannot be used while that exclusive borrow i…
  • Mutex: Mutex controls shared access with a lock. lock returns a Result containing a guard or a poisoning error; dropping a held guard unlocks it. Exclusive ownership…
  • Option: Option represents a possibly absent value as Some(value) or None. Match, combinators or ? can handle absence; unwrap can instead panic. Option makes absence ex…
  • Ownership: Owned values have responsibility for cleanup. Moving a non-Copy value transfers that responsibility and invalidates the moved-from place until reinitialized; v…
  • panic!: A failure signal commonly used for violated assumptions. Depending on compilation and runtime settings, a panic unwinds the current thread or aborts the proces…
  • Pattern: A structure matched against a value: literals, ranges, tuples, struct or enum variants, alternatives with |, and bindings with @. A match-arm guard such as if…
  • RAII: Resource Acquisition Is Initialization: a resource is tied to a value's lifetime, acquired when the value is created and released when it is dropped. Rust…
  • Reference (&T): A shared borrow, written &T, gives access without taking ownership. Several shared borrows may coexist. Ordinary data cannot be changed through &T; interio…
  • Result: The enum Result<T, E> is Ok(value) or Err(error) : a computation that can fail with a reason. Rust's recoverable error handling, used instead of exce…
  • self / Self: Lowercase self is the receiver of a method (the value it is called on); capitalized Self is an alias for the type being implemented.
  • Send & Sync: Send marks types whose ownership can safely cross threads; Sync means shared references can safely cross threads. These traits combine with borrowing to preven…
  • Shadowing: A new let binding reuses a name and hides the older binding. The new value can have a different type. Each binding independently chooses whether to use mut.
  • Slice (&[T]): The unsized type [T] describes a contiguous sequence. A borrowed slice &[T] carries a pointer and length without owning its elements; &mut [T] permits…
  • Smart pointer: A type that acts like a pointer but adds ownership semantics: Box (single owner on the heap), Rc/Arc (shared ownership), and others, often paired with a guard…
  • Statement: Code that introduces an item or binding, or evaluates an expression for its effect. let statements end in semicolons; item definitions need not. A semicolon af…
  • str / &str: str is an unsized UTF-8 text type. &str is a shared borrowed view of such text; String owns a growable buffer. String literals have type &'static s…
  • String: An owned, growable, heap-allocated, UTF-8 text buffer. Build it with push_str / push ; it is to text what Vec is to elements.
  • Struct: A named bundle of fields of fixed types. Construct with Name { field: value } , read with a dot. Tuple structs name fields by position instead.
  • Thread: thread::spawn starts an OS thread and returns a JoinHandle. join waits and returns Ok(worker_value) or an Err carrying a panic payload. Spawned closures must s…
  • Trait: A contract of method signatures a type promises to provide, like an interface. Types implement a trait with impl Trait for Type , and code can be written again…
  • Trait bound: A constraint on a generic type, like T: PartialOrd , stating which traits T must implement (and therefore what it can do). Multiple bounds combine with + .
  • Trait object (dyn): A reference or owning pointer such as &dyn Trait or Box can erase a concrete type behind a dyn-compatible trait and its required lifetime bounds. Calls use…
  • Tuple: A fixed-length group of values of possibly different types, like (i64, f64) . Access fields by position ( .0 , .1 ) or destructure with a pattern.
  • unsafe: A block or function that permits certain operations whose safety conditions the compiler cannot fully check, such as dereferencing raw pointers or calling unsa…
  • unwrap / expect: unwrap() extracts the value from an Option / Result , panicking on None / Err ; expect(msg) does the same with a custom message. Prefer match , ? , or unwrap_o…
  • Variant: One of the named cases of an enum. A variant can be a unit (no data), tuple-style ( Circle(f64) ), or struct-style ( Move { x, y } ).
  • Vec: A growable, heap-allocated array, Vec<T> . Push and pop at the end, index with [i] , and iterate; it owns its elements.
  • Zero-cost abstraction: A design goal that useful abstractions add no avoidable overhead compared with equivalent lower-level code. Static generics and iterator optimization support t…