Introduction
Why Rust
- Advanced mode will can unlock all the optimizations you could ever need
- Easy mode can be as ergonomic as Python
- It will teach you about pointers and other details about how memory works you may never learn from other languages
- How to speak Rust • No Boilerplate 📺
- Rust makes you feel like a GENIUS • No Boilerplate 📺
- Rust Is Easy • The compiler teaches you • No Boilerplate 📺
- Rust for the impatient • No Boilerplate 📺
- What makes Rust different? • No Boilerplate 📺
- Rust: Your code can be PERFECT • No Boilerplate 📺
- Stop writing Rust • In other languages it’s easy to START projects, but in rust, it’s easy to FINISH them • No Boilerplate 📺
- Rust makes cents • Rust saves cloud computing costs (by being extremely CPU and memory efficent) and the engineering time that would otherwise be spent resolving production bugs • No Boilerplate 📺
- Rust is boring • Why Rust’s superior speed and reliability now make it the best language to choose for almost any project, especially backend web development • No Boilerplate 📺
- Rust is not a faster horse • What makes Rust fundamentally different from C, C++ and other languages that came before it • No Boilerplate 📺
- Rust is easy… (we make it hard) • Let’s Get Rusty 📺
- When to Choose Rust • Nice walkthroughs of Rust’s comparative readability, safety, cloud CPU and memory cost savings, and supportive community • Tim McNamara 📺
Learning Rust
- How to learn Rust • Suggests skimming The Book, then reading it again slowly with the quizzes, then reading Rust By Example, then completing the
rustlings
katas (and repeat your favourites weekly from there) • No Boilerplate 📺 - Rust by Example • Beginner-friendly guide with interactive code examples • Rust 📕
- Easy Rust • Beginner-friendly guide, also available as a video playlist • David MacLeod 📕 📺
- The Rust Programming Language • “The Book”, also available with quizzes • Rust 📕
- rustlings • Code katas that teach you Rust by having you fix compile-time errors • Rust 🛠
- Learn Rust with Rustlings • 3 hr video walkthrough • Chris Biscardi 📺
- Rust exercises • Exercism 🛠️
- advent-of-code-rust • Template repo for solving Advent of Code puzzles using Rust • Felix Spöttel 🛠
- Rust code katas • Code Wars 🛠
- Write Your First Program with the Rust Language • Pascal Precht 📺
- Tour of Rust • Richard Anaya 📕
- A half-hour to learn Rust • Amos Wenger 📖
- Rust By Practice 📕
- The Rust Book Playlist • Follows The Book chapter-by-chapter • Let’s Get Rusty 📺
- My path to becoming a Rustacean • Prashanth Rao 📖
- Learning Rust the wrong way • Learning feels harder but is more effective when we mix topics and learning modes (read, watch, build, formulate questions, teach) over a long period of time; resist the illusion of mastery that comes from focusing on one topic or learning mode at a time; rustlings, migrating existing projects to Rust, and teaching as you go are good options • Ólafur Waage 📺
Using Rust in Easy Mode
- Features like references, lifetimes and other optimizations are optional!
- Rust can be as easy to use as Python for quick exploration and hacking if you wait to optimize
- At first, explore quickly by using
unwrap()
andclone()
everywhere - Later, optimize for safety by refactoring
unwrap
s to enums to handle potential edge cases - Later, optimize for performance by refactoring
clone
s to use references, lifetimes, etc if measurably necessary (which it likely won’t be) - How to learn Rust • At first, copy and clone everything (Rust is fast enough), pass owned variables in and out of functions, and generally avoid references (and therefore lifetimes and the borrow checker) unless the compiler tells you to use them • No Boilerplate 📺
- Easy Mode Rust • At first, clone everywhere (e.g. clone the thing you’re iterate over), pass owned instances instead of references into functions, wrap function parameters with
Arc<>
if you need to mutate them, avoid implementing traits when possible (try to stick to deriving existing traits), avoid async if sync can possibly suffice, and see how far you can get with just theVec
andHashMap
data structures • Andre Bogus 📕
General
- The Rust Project • Rust hub, maintained by the Rust team • Rust 📚
- Rust Playground • Online Rust REPL (could also use Replit) • Rust 🛠️
- Operators and Symbols Glossary • The Rust Book 📕
- Keywords Glossary • The Rust Book 📕
- The Rust Standard Library • Offical API docs • Rust 📚
- Rust error codes index - Error codes index • An in-depth explanation of each Rust compiler error • Rust 📚
- The Rust Reference • A more detailed and comprehensive version of “The Book” • Rust 📕
- The Complete Rust Programming Reference Guide • Rahul Sharma, Vesa Kaihlavirta, Claus Matzinger 📕
- Comprehensive Rust • Google Android team 📕
- The rustc Book • How to customize the Rust compiler’s behaviour • Rust 📕
- All Rust features explained • • Explains several key features by showing what they look like in the language they originally came from • Let’s Get Rusty 📺
Updates & Roadmap
- Rust 2024 and beyond • Nicholas Matsakis 📺
- Rust Blog
Sources of Rust Content
- Rust • YouTube channel 📺
- Rust conference channels…
- No Boilerplate • YouTube channel 📺
- Let’s Get Rusty • YouTube channel 📺
- Jon Gjengset • YouTube channel 📺
- Chris Biscardi • YouTube channel 📺
Dev Tools
- Install Rust • Rust 📚
- Tools • Officially recommended tools for Rust projects • Rust 📚
- Build your Rust lightsaber • Recommended tools for Rust projects • No Boilerplate 📺
- Rust in Visual Studio Code • Visual Studio Code 📚
- rust-analyzer • Visual Studio Marketplace 🛠️
- rust.vim • Official Vim configuration for Rust • Rust 🛠
- rustfmt • Official Rust formatter that enforces these style rules • Rust 🛠
- rust-clippy • Official linting helper that catches these common mistakes • Rust 🛠
- awesome-rust • A curated list of Rust code and resources 🛠️
- Develop your Rust application • May want to use scratch image instead • Docker Docs 📚
New Project Stack
- Rust: rustfmt, rust-clippy
- Docker Compose: …
- Observability: tracing, Slack
- Database: postgres, sqlx
- Data manipulation: polars
Crates
- polars • Manipulate tabular data • Polars 🛠️
- rand • Generate random values 🛠️
- rayon • Iterate in parallel 🛠️
- regex - crates.io: Rust Package Registry
- serde • Serialize and deserialize to/from JSON and other formats 🛠️
- time • Measure and format date and time (see chrono if you need more) 🛠️
Variables
let type_inferred = 'immutable'
let type_explicit: &str = 'immutable'
let mut will_change = 'mutable'
// Variable names can be "shadowed"
let shadowed = 69 // unusable soon
let shadowed = true // type can change
Functions
// Every crate starts here
fn main() {}
// Arguments can be owned or borrowed
fn owns_input(arg: String) {}
fn borrows_input(arg: &String) {}
fn mutates_input(arg: &mut String) {}
// Functions can be unnamed
fn contains_closures() {
let x = 1
let y = 2
// Both referred to as "closures"
let closure = |z| x + y + z
let anonymous = |z| z + 3
}
Types
- A JavaScript developer’s first step to understanding Rust types • Chris Biscardi 📺
Numbers
u8
u16
u32
u64
u128
represent non-negative whole numbersi8
i16
i32
i64
i128
for representing whole numbers- pointer sized integers -
usize
isize
for representing indexes and sizes of things in memory - floating point -
f32
f64
Collections
Strings
// Create a string
let s: &str = "hello"; // a slice
let s: String = "hello".to_string();
let s: String = String::from("hello");
// Create a slice from a String
let s_ref = &s; // "hello"
let s_slice_full = &s[..]; // "hello"
let s_slice_part = &s[..4]; // "hell"
&str
String
values are stored on the heap- A slice is a reference to a range of
u8
bytes (ascii characters) in aString
or items in aVector
- The range specifies the index of the first character and the index after the last character
- The slice reference points directly to the substring data on the heap
- A slice gains “read” and “own” permissions and removes the original string pointer’s “write” and “own” permissions
- A slice includes a
len
property - One advantage of slices over index-based ranges is that the slice cannot be invalidated while it’s being used
- Useful methods:
- (similar to Vecs, since they are implemented as a
Vec<u8>
) capacity()
• How many bytes are allocatedchars().count()
pop()
• ReturnOption<char>
of last characterretain()
• Same as filter, pass in a closure that takes a character and returns true if it should stayshrink_to_fit()
• Reduce memory allocated
- (similar to Vecs, since they are implemented as a
- Working with strings in Rust • Amos Wenger 📖
- Rust Pizza Slices, Arrays and Strings • Array and string slices • Code to the Moon 📺
Vectors
- Vec • Rust Standard Library 📚
- Useful methods:
- dedup • Remove sequential duplicates (sort first to remove all)
- sort
- sort_unstable
// Create an array slice
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
assert_eq!(slice, &[2, 3]);
Enums
- Enums are custom types that can be one of a set of enumerated values
Option<T>
helps you use the type system to prevent errors- When enum values have data inside them, you can use
match
orif let
to extract and use those values (depending on how many cases you need to handle)- If the function only has an effect in one condition, an
if let
is most idiomatic - If the function needs to return a value for each condition, a
match
is most appropriate
- If the function only has an effect in one condition, an
- Enum variants are public by default
- Enums and Pattern Matching • The Rust Programming Language 📕
- Option • Rust Standard Library 📚
- Either • Rust crate 📦
- Rust’s Most Important Containers 📦 10 Useful Patterns •
Option
andResult
• Code to the Moon 📺
Structs
- Each field of a struct is private unless annotated with
pub
- Using Structs to Structure Related Data • The Rust Programming Language 📕
HashMap
- hashbrown • More performant drop-in replacement for
HashMap
andHashSet
Iterators
Example from Learning Rust via Advent of Code:
let slot = units
.iter()
.filter(|unit| self.is_enemy(u)) // ignore non-enemies
.flat_map(|unit| unit.pos.adjacent_positions(w,h)) // iterate adjacent positions
.filter(|pos| !walls[pos] && !occupied[pos]) // ignore walls or occupied spaces
.min_by_key(|pos| (self.pos - pos).length_sq()); // pick closest slot
- Iterator in std::iter - Rust
- lazy
- Methods:
- collect - transform iterator into a collection; such as a Vec
- count - counts iterations to reach end
- cycle - creats iterator that loops
- enumerate - creates iterator that returns tuple of iteration count and value
- filter - uses closure to yield values
- find - searches for value
- flat_map - map plus flatten
- fold - applies function to produce a single value
- for_each - calls closure for each value
- map - transforms each value into another
- max - returns largest value in iteration
- max_by - returns largest value using provided comparison function
- min_by_key - returns value given a function
- sum - returns sum of iterator values
Data Modeling
- Rust: No Classes, No Problem • Make invalid states unrepresentable by creating state machines with enums and match statements • No Boilerplate 📺
- Building a space station in Rust • Define your possible states, then the functions for moving between them • No Boilerplate 📺
Control flow
match
- The match Control Flow Construct • The Rust Programming Language 📕
- Combining
match
and enums is useful in many situations:match
against an enum, bind a variable to the data inside, then execute code based on it - Matches are exhaustive: the arms’ patterns must cover all possibilities
- In the case of
Option<T>
, Rust ensures we explicitly handle theNone
case so we don’t assume we have a value that might be empty - The last match case can be a catch-all case for all remaining values (using a named variable like
other
if the value will be used or_
if it won’t be) - Match on a reference to the variable to avoid moving its ownership to the match handlers
if let
- Concise Control Flow with if let • The Rust Programming Language 📕
- Using
if let
means less typing, less indentation, and less boilerplate code; however, you lose the exhaustive checking thatmatch
enforces - Choosing between
match
andif let
depends on whether gaining conciseness is an appropriate trade-off for losing exhaustive checking - Rust Branching - if let, match • Code to the Moon 📺
Error Handling
?
operator- Announcing Rust 1.13 • Rust Blog 📖
Ownership
- Ownership is a big deal in Rust!
- It’s a core concept that makes Rust fundamentally different from other languages
- It results in the compiler forcing you to solve more potential errors ahead of time, in exchange for encountering fewer errors at runtime
- Ownership Recap • The Rust Programming Language 📕
- Ownership Inventory • The Rust Programming Language 📕
References
From The Rust Programming Language, Chapter 4.
- References are non-owning pointers
- They allow reading and writing data without consuming its ownership
- References are created with borrows (
&
and&mut
) and used with dereferences (*
), often implicitly - Rust’s borrow checker enforces a system of permissions (read, write, own, flow) that ensures references are used safely:
- Creating a reference will transfer (some or all) permissions from the borrowed path to the reference
- Permissions are returned once the reference’s lifetime (usage) has ended
- Data must outlive (not be dropped during the lifetime of) all references that point to it
- If you want to pass around a reference to a value, you have to make sure that the underlying value lives long enough
- Fix: use/return the original path instead of a reference to it (move ownership)
- Fix: use/return a static literal value instead of a value allocated to the heap (if the data will never change)
- Fix: defer borrow-checking to runtime by cloning a reference-counted pointer to the original path (via
Rc::clone
), which will wait to deallocate the data until the lastRc
pointing to it has been dropped (opt into garbage collection) - Fix: shorten the borrower’s lifetime so the data can be safely changed/dropped
- Copying a value without moving its ownership:
- If a value is immutable and does not own heap data, it can be copied without moving its ownership
- If a variable pointing to heap data could be copied without a move, then two variables could think they own the same data, leading to a double-free
- Examples of types that own heap data (and therefore do not implement the
Copy
trait):String
- Examples of types that do not own heap data (and therefore do implement the
Copy
trait):i32
,&String
- The compiler will not allow two mutable references to the same value to exist at the same time
- Dereferencing a heap value tries to take ownership of it, but ownership cannot be taken through a reference (a non-owning pointer)
- The compiler will fail with “cannot move out of…a shared reference” when trying to copy values stored on the heap to avoid two values thinking they own the same data, which would later result in trying to deallocate the same heap memory twice
- Safe options for accessing heap values (options that don’t move ownership):
- Clone the data
- Use an immutable reference
- Unsafe options for accessing heap values (options that would move ownership):
- …
- Rust Demystified 🪄 Simplifying The Toughest Parts • Demonstrating how references and lifetimes work by repeatedly refactoring a short code example • Code to the Moon 📺
- Rust’s Alien Data Types 👽 Box, Rc, Arc • Examples of when using a smart pointer can solve a problem • Code to the Moon 📺
- Rust Interior Mutability - Sneaking By The Borrow Checker • How to work around the limitations of the Rust borrow checker using
Cell
,RefCell
,RwLock
andMutex
• Code to the Moon 📺 - What’s a reference in Rust? • Julia Evans 📖
- Rust pattern: Rooting an Rc handle • Sometimes it is useful to clone the data you are iterating over into a local variable, so that the compiler knows it will not be freed; if the data is immutable, storing that data in an
Rc
orArc
makes that clone cheap (i.e., O(1)) • Baby Steps 📖
Async
- Have recent updates made async easier than it used to be…? Less need for third party solutions?
- Asynchronous Programming in Rust • Rust 📕
- Getting started with Tokio. The ultimate starter guide to writing async Rust. • Dreams of Code 📺
- Async-await on stable Rust! • Rust Blog 📖
Debugging
println!
macrodbg!
macro- Easy Rust 195: Backtraces at runtime • Use
std::env::set_var("RUST_BACKTRACE", "1")
to enable compile time backtraces on panics +std::backtrace::Backtrace::capture()
to capture at specific points (orforce_capture()
to capture regardless of the env var setting) • David MacLeod 📺
Testing
- Rust Tests Itself (kind of!) • No Boilerplate 📺
- How to organize your Rust tests • LogRocket 📖
- Mocking Rust 🤪 and Testing 🧪 • Code to the Moon 📺
Observability
- Are we observable yet? An introduction to Rust telemetry • Luca Palmieri 📖
- tracing crate
- use instead of using log directly 🛠️
- enable log feature in
Cargo.toml
to automatically emit logs whenever events or spans are created - use tracing::span to capture events along with their durations and any relevant metadata (e.g. variable values at the time)
- use tracing::Instrument to attach a spans to a
Future
- use tracing::Subscriber to …
- use tracing_opentelemetry to emit traces in an OpenTelemetry-compatible format (e.g. so they can be ingested by Honeycomb for further analysis)
- Rust • OpenTelemetry 📚
- Getting Started Instrumenting Rust OpenTelemetry 📚
- Collect Metrics, Logs, and Traces from Your Rust Applications with OpenTelemetry • Florian Tieben 📖
- Rust Telemetry Workshop • Mainmatter 🛠️
Modules, Crates, Packages & Workspaces
- Rust lets you split a package into multiple crates and a crate into modules so you can refer to items defined in one module from another module by specifying absolute or relative paths
- Paths can be brought into scope with a
use
statement so you can use a shorter path for multiple uses of the item in that scope - Module code is generally private by default, but you can make definitions public by adding the
pub
keyword - Paths: A way of naming an item, such as a struct, function, or module
- absolute (starting with
crate
) - relative (possibly starting with
super
) use
keyword- shorten paths by defining a shortcut
- for functions, create a shortcut to the parent module so function calls will start with it make it clear the function isn’t locally defined
- for all other types, create a shortcut to the item itself
- naming collisions can be solved either by stopping at the parent module or creating an alias with the
as
keyword
- absolute (starting with
- Modules and use: Let you control the organization, scope, and privacy of paths
mod
keywordpub
keyword- Rust modules vs files • Faster than Lime 📖
- Crates: A tree of modules that produces a library or executable
- Packages: A Cargo feature that lets you build, test, and share crates
- External packages are available at crates.io
- Using them involves two steps: listing them in your package’s Cargo.toml and bringing their items into scope with
use
- Managing Growing Projects with Packages, Crates and Modules • The Rust Programming Language 📕
- Unboxing Rust Crates, Packages, Modules & Workspaces • Code to the Moon 📺
- Rust API Guidelines • How to manage the changes you make to crates you’ve published for others to use • Rust 📕
Functional Paradigm
- Rust’s Hidden Purity System • Particularly
const
functions • No Boilerplate 📺 - Rust on Rails • How to write rust code that never crashes by using the
Result
type to implement the railway pattern, which eliminatesnull
return values by enforcing error awareness and handling • No Boilerplate 📺
Macros
- Introduction - The Little Book of Rust Macros • Daniel Keep & Lukas Wirth 📕
- Ace Rust Macros ♠️ the declarative kind • Code to the Moon 📺
Performance
- The Rust Performance Book • Nicholas Nethercote 📕
- Profiling:
- Cargo includes a benchmarking tool
- criterion - Rust
- flame - crates.io: Rust Package Registry
- flamer - crates.io: Rust Package Registry
- Compile time:
- Stupidly effective ways to optimize Rust compile time • XX Chan 📖
- Are we fast yet (.rs)
Cargo
- Commands:
- cargo init • The Cargo Book 📕
- cargo install • The Cargo Book 📕
- cargo update • The Cargo Book 📕
- cargo check • The Cargo Book 📕
- cargo test • The Cargo Book 📕
- cargo bench • The Cargo Book 📕
- The Cargo Book • A book on Rust’s package manager and build system • Rust 📕
Use Cases
Building CLIs
- Command-line apps • Overview of why Rust is a good fit, with links to other resources • Rust 📚
- Command Line Book • How to build effective command line applications in Rust • Rust 📚
- Rust Command Line Argument Parsing (A Better Way With Clap) • Automatic parsing and help generation • Code to the Moon 📺
- Should you use StructOpt or Clap for Rust CLIs | Release Radar • How
clap
(which is used bycargo
andripgrep
) makes parsing CLI args much easier • Chris Biscardi 📺
Building Web Backends
- Zero To Production In Rust - An Introduction To Backend Development • Luca Palmieri 📕
- Statically Typed APIs with Poem and Rust • Backend web development stack recommendations • No Boilerplate 📺
- Tide is perhaps my favorite Rust web app framework. • Dreams of Code 📺
- Build A Rust Backend (Really FAST Web Services with Actix Web) • Code to the Moon 📺
- Networking - Rust Programming Language • Overview of why Rust is a good fit, with links to other resources • Rust 📚
- reqwest • Rust Standard Library 📚
- Rocket • Simple, fast, type-safe web framework for Rust • Rocket 🛠
- Build Auth Into Your Rust Web Application (OAuth2) • With Yew-Oath2 and Actix Web • Code to the Moon 📺
- Leptos (full-stack framework):
- The Leptos Book • Leptos 📕
- Build A Rust-Powered Journaling App (with Upstash Redis) • Leptos and Redis • Code to the Moon 📺
- Are we web yet? Yes, and it’s freaking fast!
- Working with a database:
- HTML forms, Databases, Integration tests • Use
postgres
via Docker andsqlx
• Luca Palmieri 📖 - SQLx is my favorite PostgreSQL driver to use with Rust. • Dreams of Code 📺
- Rust & SQL Databases (With Diesel) • Code to the Moon 📺
- HTML forms, Databases, Integration tests • Use
Building Web Frontends
- Rust and WebAssembly Book • How to use Rust to build browser-native libraries through WebAssembly • Rust 📚
- Web Apps with WebAssembly • Overview of why Rust is a good fit, with links to other resources • Rust 📚
- Rust & Wasm • The advantages of using Rust to build web apps that can be deployed anywhere (by transpiling to WASM) • No Boilerplate 📺
- Web-native Rust apps [PART 2] • No Boilerplate 📺
- Yew • React-like framework for building component-based frontends using Rust • Yew 🛠️
- Full Stack Rust Webapp + Docker Image Build • Yew frontend, Artix backend, shared Cargo workspace • Code to the Moon 📺
- Build A Rust Frontend (Really FAST Web Apps with Yew) • Code to the Moon 📺
- Leptos (full-stack framework):
- Build A Full Stack Chatbot in Rust (feat. Leptos & Rustformers) • Code to the Moon 📺
- Dioxus (full-stack framework):
- Rust full stack web frameworks have a bright future • Contrasts Dioxus (React-like) vs Leptos (Solid-like); at first glance, Leptos’ syntax looks more familiar and attractive to me • Code to the Moon 📺
- Modern All Rust Stack - Dioxus, Axum, Warp, SurrealDB • Builds a notes web app with Rust • Code to the Moon 📺
- Zola • Static site generator 🛠
Inbox
- rust pros: it prioritizes safety, which I always focus on when using TS/python etc; I value spending time up front to prevent runtime bugs
- Learning Rust via Advent of Code - Forrest Smith