Rust Cheat Sheet

A comprehensive quick reference for the Rust programming language. Covers variables, ownership, borrowing, traits, error handling, iterators, concurrency, and more.

Variables & Constants

Pattern Description Example
let Immutable variable binding (default) let x = 5;
let mut Mutable variable binding let mut x = 5; x = 10;
const Compile-time constant (must annotate type) const MAX_SIZE: u32 = 1000;
static Static lifetime variable (global) static VERSION: &str = "1.0";
static mut Mutable static (requires unsafe) static mut COUNT: u32 = 0;
Shadowing Re-declare a variable with the same name let x = 5; let x = x + 1; let x = "now a string";
Type annotation Explicitly specify the type let x: i32 = 42;
Destructuring Bind multiple variables at once let (x, y, z) = (1, 2.0, "three");

Data Types

Type Description Example
i8, i16, i32, i64, i128 Signed integers (8 to 128 bit) let x: i32 = -42;
u8, u16, u32, u64, u128 Unsigned integers (8 to 128 bit) let x: u8 = 255;
isize, usize Platform-dependent size (pointer width) let idx: usize = 0;
f32, f64 Floating-point numbers (32 and 64 bit) let pi: f64 = 3.14159;
bool Boolean (true or false) let active: bool = true;
char Unicode scalar value (4 bytes) let c: char = 'R';
String Heap-allocated, growable UTF-8 string let s = String::from("hello");
&str String slice (borrowed reference to string data) let s: &str = "hello";
Tuple Fixed-size collection of mixed types let t: (i32, f64, &str) = (1, 2.0, "hi");
Array Fixed-size collection of same type let a: [i32; 5] = [1, 2, 3, 4, 5];
Slice Reference to a contiguous sequence let slice: &[i32] = &a[1..3];
Unit type Empty tuple, like void let unit: () = ();

Control Flow

Construct Description Example
if / else Conditional branching (no parens needed) if x > 5 { "big" } else { "small" }
if let Pattern match in conditional if let Some(v) = opt { println!("{v}"); }
loop Infinite loop (break to exit) loop { break; }
loop with value Return a value from a loop via break let x = loop { break 42; };
while Conditional loop while x < 10 { x += 1; }
while let Loop while pattern matches while let Some(v) = stack.pop() { ... }
for Iterate over a range or iterator for i in 0..10 { println!("{i}"); }
for (inclusive) Inclusive range with ..= for i in 0..=10 { /* 0 to 10 */ }
match Exhaustive pattern matching match x { 1 => "one", 2 => "two", _ => "other" }
Loop labels Named loops for nested break/continue 'outer: loop { break 'outer; }

Functions & Closures

Pattern Description Example
Function Declare with fn, specify param and return types fn add(a: i32, b: i32) -> i32 { a + b }
Implicit return Last expression without ; is the return value fn double(x: i32) -> i32 { x * 2 }
Early return Explicit return keyword fn check(x: i32) -> bool { if x < 0 { return false; } true }
No return value Returns unit () implicitly fn greet(name: &str) { println!("Hi {name}"); }
Closure Anonymous function that captures environment let add = |a, b| a + b;
Typed closure Closure with explicit type annotations let add = |a: i32, b: i32| -> i32 { a + b };
move closure Force closure to take ownership of captured variables let s = String::from("hi"); let f = move || println!("{s}");
Fn traits Fn, FnMut, FnOnce -- how closures capture fn apply(f: impl Fn(i32) -> i32, x: i32) -> i32 { f(x) }
Function pointer Pass a named function as a value fn square(x: i32) -> i32 { x * x }
let f: fn(i32) -> i32 = square;

Ownership & Borrowing

Concept Description Example
Ownership Each value has exactly one owner let s1 = String::from("hi"); let s2 = s1; // s1 is moved
Move Assignment transfers ownership (heap types) let v1 = vec![1, 2]; let v2 = v1; // v1 invalid
Clone Deep copy to avoid move let v1 = vec![1, 2]; let v2 = v1.clone(); // both valid
Copy Stack types implement Copy (auto-duplicated) let x = 5; let y = x; // both valid (i32 is Copy)
Immutable borrow Multiple &T references allowed simultaneously let s = String::from("hi"); let r1 = &s; let r2 = &s;
Mutable borrow Only one &mut T at a time, no other refs let mut s = String::from("hi"); let r = &mut s;
Lifetime annotation Explicit lifetime to link reference lifetimes fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { ... }
'static lifetime Reference valid for entire program duration let s: &'static str = "lives forever";
Lifetime elision Compiler infers lifetimes in common patterns fn first(s: &str) -> &str { &s[..1] } // lifetime inferred

Structs, Enums & Traits

Pattern Description Example
Struct Named fields struct Point { x: f64, y: f64 }
Tuple struct Unnamed fields (newtype pattern) struct Meters(f64);
Unit struct No fields (marker type) struct Marker;
impl block Methods and associated functions impl Point { fn new(x: f64, y: f64) -> Self { Self { x, y } } }
&self method Borrows self immutably fn distance(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() }
&mut self method Borrows self mutably fn translate(&mut self, dx: f64) { self.x += dx; }
Enum Type with multiple variants enum Color { Red, Green, Blue }
Enum with data Variants can hold data enum Shape { Circle(f64), Rect { w: f64, h: f64 } }
Trait Define shared behavior (like interfaces) trait Greet { fn hello(&self) -> String; }
impl Trait for Implement a trait for a type impl Greet for Point { fn hello(&self) -> String { format!("({}, {})", self.x, self.y) } }
Default methods Traits can provide default implementations trait Greet { fn hello(&self) -> String { "Hi!".into() } }
Derive macros Auto-implement common traits #[derive(Debug, Clone, PartialEq)]
struct Point { x: f64, y: f64 }
impl Trait (arg) Accept any type implementing a trait fn print_it(item: &impl Display) { println!("{item}"); }
Trait bounds Generic constraint with trait fn print_it<T: Display + Debug>(item: &T) { ... }
where clause Cleaner trait bounds syntax fn process<T>(item: T) where T: Display + Clone { ... }
dyn Trait Trait object for dynamic dispatch fn log(writer: &dyn Write) { ... }

Option & Result

Type / Method Description Example
Option<T> Some(T) or None -- nullable values let x: Option<i32> = Some(42);
Result<T, E> Ok(T) or Err(E) -- recoverable errors let r: Result<i32, String> = Ok(42);
.unwrap() Get value or panic if None/Err let v = some_option.unwrap();
.expect("msg") Like unwrap but with custom panic message let v = some_result.expect("failed to read");
.unwrap_or(default) Get value or use a default let v = opt.unwrap_or(0);
.unwrap_or_else(f) Get value or compute default from closure let v = opt.unwrap_or_else(|| compute_default());
.is_some() / .is_none() Check if Option contains a value if opt.is_some() { ... }
.is_ok() / .is_err() Check if Result is success or error if result.is_ok() { ... }
.map(f) Transform the inner value let doubled = Some(5).map(|x| x * 2); // Some(10)
.and_then(f) Chain operations that return Option/Result let r = opt.and_then(|x| if x > 0 { Some(x) } else { None });
.ok() Convert Result to Option (discards error) let opt: Option<i32> = result.ok();
.transpose() Swap Option and Result nesting let x: Result<Option<i32>, E> = Some(Ok(5)).transpose();

Error Handling

Pattern Description Example
? operator Propagate error early (returns Err to caller) let content = std::fs::read_to_string("file.txt")?;
? with Option Also works with Option (returns None) fn first_char(s: &str) -> Option<char> { s.chars().next() }
Custom error type Define your own error enum #[derive(Debug)]
enum AppError { Io(std::io::Error), Parse(std::num::ParseIntError) }
impl Display Human-readable error message impl fmt::Display for AppError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ... } }
impl From Auto-convert errors for ? impl From<std::io::Error> for AppError { fn from(e: std::io::Error) -> Self { AppError::Io(e) } }
Box<dyn Error> Quick and flexible error type fn run() -> Result<(), Box<dyn std::error::Error>> { ... }
panic!() Unrecoverable error -- crashes the thread panic!("something went terribly wrong");
unreachable!() Mark code that should never be reached _ => unreachable!("invalid state")
todo!() Placeholder for unfinished code fn not_yet() -> i32 { todo!() }

Collections

Collection Description Example
Vec<T> Growable array (most common collection) let mut v = vec![1, 2, 3];
v.push(4);
Vec::new() Create empty Vec let mut v: Vec<i32> = Vec::new();
Vec::with_capacity(n) Pre-allocate capacity let mut v = Vec::with_capacity(100);
HashMap<K, V> Key-value hash map use std::collections::HashMap;
let mut m = HashMap::new();
m.insert("key", 42);
.entry().or_insert() Insert if key missing m.entry("key").or_insert(0);
HashSet<T> Unique value set (fast lookup) use std::collections::HashSet;
let mut s = HashSet::new();
s.insert(42);
BTreeMap<K, V> Sorted key-value map (by key order) use std::collections::BTreeMap;
let mut m = BTreeMap::new();
BTreeSet<T> Sorted unique value set use std::collections::BTreeSet;
VecDeque<T> Double-ended queue use std::collections::VecDeque;
let mut dq = VecDeque::new();
dq.push_back(1); dq.push_front(0);
LinkedList<T> Doubly-linked list (rarely needed) use std::collections::LinkedList;

String Handling

Operation Description Example
String vs &str Owned (heap) vs borrowed (reference) string let owned = String::from("hi"); let borrowed: &str = "hi";
.to_string() Convert &str to String let s: String = "hello".to_string();
.as_str() Borrow String as &str let s = String::from("hi"); let r: &str = s.as_str();
format!() Build String from format template let s = format!("{} is {} years old", name, age);
Concatenation Use + or format!() let s = s1 + " " + &s2; // s1 is moved
let s = format!("{s1} {s2}"); // no move
.push_str() Append a string slice let mut s = String::from("hello"); s.push_str(" world");
.push() Append a single character s.push('!');
.contains() Check if string contains substring "hello world".contains("world") // true
.replace() Replace occurrences "hello".replace("l", "r") // "herro"
.split() Split into iterator "a,b,c".split(',').collect::<Vec<&str>>()
.trim() Remove leading/trailing whitespace " hello ".trim() // "hello"
.len() Byte length (not char count) "hello".len() // 5
.chars().count() Actual character count "cafe\u{0301}".chars().count() // 5
.parse() Parse string into another type let n: i32 = "42".parse().unwrap();

Iterators & Combinators

Method Description Example
.iter() Iterate by reference (&T) for val in vec.iter() { ... }
.iter_mut() Iterate by mutable reference (&mut T) for val in vec.iter_mut() { *val += 1; }
.into_iter() Iterate by value (consumes collection) for val in vec.into_iter() { ... }
.map(f) Transform each element vec![1,2,3].iter().map(|x| x * 2).collect::<Vec<_>>()
.filter(f) Keep elements matching predicate vec![1,2,3,4].iter().filter(|&&x| x > 2).collect::<Vec<_>>()
.filter_map(f) Filter + map in one step vec!["1","hi","3"].iter().filter_map(|s| s.parse::<i32>().ok()).collect::<Vec<_>>()
.fold(init, f) Accumulate into single value vec![1,2,3].iter().fold(0, |acc, x| acc + x) // 6
.sum() Sum all elements let total: i32 = vec![1,2,3].iter().sum();
.collect() Consume iterator into a collection let v: Vec<i32> = (0..10).collect();
.enumerate() Pair each element with its index for (i, val) in vec.iter().enumerate() { ... }
.zip(other) Pair elements from two iterators let pairs: Vec<_> = a.iter().zip(b.iter()).collect();
.chain(other) Concatenate two iterators let all: Vec<_> = a.iter().chain(b.iter()).collect();
.take(n) Take first n elements (0..).take(5).collect::<Vec<_>>() // [0,1,2,3,4]
.skip(n) Skip first n elements (0..10).skip(5).collect::<Vec<_>>() // [5,6,7,8,9]
.flat_map(f) Map then flatten one level vec![vec![1,2], vec![3]].into_iter().flat_map(|v| v).collect::<Vec<_>>()
.any(f) / .all(f) Check if any/all elements match predicate vec![1,2,3].iter().any(|&x| x > 2) // true
.find(f) First element matching predicate vec![1,2,3].iter().find(|&&x| x == 2) // Some(&2)
.position(f) Index of first match vec![1,2,3].iter().position(|&x| x == 2) // Some(1)
.min() / .max() Minimum / maximum element vec![3,1,2].iter().max() // Some(&3)

Pattern Matching

Pattern Description Example
Literal Match exact values match x { 1 => "one", 2 => "two", _ => "other" }
Variable binding Bind matched value to a name match x { n => println!("got {n}") }
Multiple patterns OR with | match x { 1 | 2 | 3 => "small", _ => "big" }
Range Match a range of values match x { 0..=9 => "digit", _ => "other" }
Destructure struct Extract struct fields match point { Point { x, y: 0 } => println!("on x-axis at {x}"), _ => {} }
Destructure enum Extract enum variant data match msg { Message::Quit => quit(), Message::Move { x, y } => move_to(x, y) }
Tuple destructuring Match tuple elements match (x, y) { (0, 0) => "origin", (x, 0) => format!("x={x}"), _ => "other".into() }
Guard clause Extra condition with if match x { n if n < 0 => "negative", n if n > 0 => "positive", _ => "zero" }
@ binding Bind a name while matching pattern match x { n @ 1..=5 => println!("got {n}"), _ => {} }
Nested patterns Match within Option/Result match opt { Some(0) => "zero", Some(n) => format!("{n}"), None => "none".into() }
Wildcard _ Ignore a value let (_, y, _) = (1, 2, 3);
if let Match a single pattern if let Some(val) = opt { println!("{val}"); }
while let Loop while pattern matches while let Some(top) = stack.pop() { println!("{top}"); }
let else Destructure or diverge (Rust 1.65+) let Some(val) = opt else { return; };

Modules & Crates

Keyword / Pattern Description Example
mod Declare a module mod utils { pub fn helper() {} }
mod (file) Module from separate file mod utils; // loads utils.rs or utils/mod.rs
pub Make item public pub fn public_fn() {}
pub(crate) Public within the crate only pub(crate) fn internal_fn() {}
pub(super) Public to parent module only pub(super) fn parent_visible() {}
use Bring item into scope use std::collections::HashMap;
use ... as Alias an import use std::collections::HashMap as Map;
use ... {} Multiple imports from same path use std::io::{self, Read, Write};
use ... * Glob import (use sparingly) use std::collections::*;
crate:: Absolute path from crate root use crate::utils::helper;
super:: Path relative to parent module use super::config::Settings;
External crate Add in Cargo.toml, then use // Cargo.toml: serde = "1.0"
use serde::{Serialize, Deserialize};

Common Traits

Trait Description Example
Debug Format with {:?} for debugging #[derive(Debug)] struct P { x: i32 }
println!("{:?}", p);
Display Format with {} for user-facing output impl fmt::Display for P { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({}, {})", self.x, self.y) } }
Clone Explicit deep copy via .clone() #[derive(Clone)] struct P { x: i32 }
let p2 = p1.clone();
Copy Implicit bitwise copy (requires Clone) #[derive(Copy, Clone)] struct P { x: i32, y: i32 }
PartialEq / Eq Equality comparison (== and !=) #[derive(PartialEq, Eq)] struct P { x: i32 }
PartialOrd / Ord Ordering comparison (<, >, etc.) #[derive(PartialOrd, Ord, PartialEq, Eq)] struct P { x: i32 }
Hash Produce a hash (needed for HashMap keys) #[derive(Hash, PartialEq, Eq)] struct Id(u64);
Default Default value via Default::default() #[derive(Default)] struct Config { verbose: bool, retries: u32 }
From / Into Type conversion (implement From, get Into free) impl From<&str> for Name { fn from(s: &str) -> Self { Name(s.to_string()) } }
let n: Name = "Alice".into();
TryFrom / TryInto Fallible type conversion impl TryFrom<i32> for Positive { type Error = String; fn try_from(n: i32) -> Result<Self, Self::Error> { ... } }
Iterator Define custom iteration impl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option<u32> { ... } }
Drop Custom cleanup when value goes out of scope impl Drop for Resource { fn drop(&mut self) { println!("freed"); } }
Deref / DerefMut Custom dereference behavior (smart pointers) impl Deref for MyBox<T> { type Target = T; fn deref(&self) -> &T { &self.0 } }

Macros

Macro Description Example
println!() Print to stdout with newline println!("x = {x}, y = {}", y);
eprintln!() Print to stderr with newline eprintln!("Error: {err}");
format!() Format into a String (no I/O) let s = format!("{name} is {age}");
dbg!() Debug print expression and value to stderr dbg!(&my_vec); // prints [src/main.rs:5] &my_vec = [...]
vec![] Create a Vec with initial values let v = vec![1, 2, 3];
let zeros = vec![0; 100]; // 100 zeros
assert!() Assert condition is true assert!(x > 0);
assert_eq!() / assert_ne!() Assert two values are equal / not equal assert_eq!(add(2, 3), 5);
todo!() Placeholder for unfinished code (panics) fn later() { todo!("implement this") }
unimplemented!() Mark intentionally unimplemented code fn optional_feature() { unimplemented!() }
cfg!() Compile-time configuration check if cfg!(target_os = "linux") { ... }
#[cfg()] Conditional compilation attribute #[cfg(test)] mod tests { ... }
Format specifiers Control formatting output println!("{:?}", val); // Debug
println!("{:#?}", val); // Pretty Debug
println!("{:.2}", 3.14159); // "3.14"
println!("{:08b}", 42); // "00101010"

Concurrency

Concept Description Example
thread::spawn Spawn a new OS thread use std::thread;
let handle = thread::spawn(|| { println!("hello from thread"); });
handle.join().unwrap();
move closure Transfer ownership into thread let data = vec![1,2,3];
thread::spawn(move || { println!("{:?}", data); });
mpsc::channel Multi-producer, single-consumer channel use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
tx.send(42).unwrap();
let val = rx.recv().unwrap();
Mutex<T> Mutual exclusion lock use std::sync::Mutex;
let m = Mutex::new(0);
{ let mut num = m.lock().unwrap(); *num += 1; }
Arc<T> Atomic reference counting for shared ownership use std::sync::Arc;
let data = Arc::new(Mutex::new(0));
let data_clone = Arc::clone(&data);
Arc<Mutex<T>> Shared mutable state across threads let counter = Arc::new(Mutex::new(0));
for _ in 0..10 {
let c = Arc::clone(&counter);
thread::spawn(move || { *c.lock().unwrap() += 1; });
}
RwLock<T> Multiple readers OR one writer use std::sync::RwLock;
let lock = RwLock::new(5);
let r = lock.read().unwrap();
// let mut w = lock.write().unwrap();
Send trait Type can be transferred between threads // Most types are Send. Rc<T> is NOT Send.
Sync trait Type can be shared between threads via reference // T is Sync if &T is Send. Mutex<T> is Sync.
Async/Await Asynchronous programming (requires runtime) async fn fetch() -> Result<String, Error> { ... }
let result = fetch().await?;

Cargo Commands

Command Description Example
cargo new Create a new project cargo new my_project
cargo new --lib my_library
cargo build Compile the project cargo build
cargo build --release
cargo run Build and run the project cargo run
cargo run -- arg1 arg2
cargo test Run tests cargo test
cargo test test_name
cargo test -- --nocapture
cargo check Type-check without producing binary (fast) cargo check
cargo add Add a dependency to Cargo.toml cargo add serde --features derive
cargo add tokio@1
cargo update Update dependencies to latest compatible versions cargo update
cargo update -p serde
cargo doc Generate documentation cargo doc --open
cargo fmt Format code with rustfmt cargo fmt
cargo fmt -- --check
cargo clippy Run the Rust linter cargo clippy
cargo clippy -- -D warnings
cargo bench Run benchmarks cargo bench
cargo publish Publish crate to crates.io cargo publish
cargo publish --dry-run
cargo install Install a binary crate cargo install ripgrep
cargo install --path .
cargo tree Display dependency tree cargo tree
cargo tree -d # show duplicates

Smart Pointers

Type Description Example
Box<T> Heap-allocated value (single owner) let b = Box::new(5);
let list = Box::new(Node { val: 1, next: None });
Rc<T> Reference counted (single-threaded shared ownership) use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
Arc<T> Atomic Rc (thread-safe shared ownership) use std::sync::Arc;
let a = Arc::new(5);
RefCell<T> Interior mutability (runtime borrow checking) use std::cell::RefCell;
let c = RefCell::new(5);
*c.borrow_mut() += 1;
Cell<T> Interior mutability for Copy types use std::cell::Cell;
let c = Cell::new(5);
c.set(10);
Cow<'a, T> Clone-on-write (borrow or own) use std::borrow::Cow;
fn process(s: Cow<str>) { ... }
Pin<P> Pin a value in memory (used with async/futures) use std::pin::Pin;
let pinned = Box::pin(my_future);

Testing

Pattern Description Example
Unit test Test function with #[test] attribute #[test]
fn test_add() { assert_eq!(add(2, 3), 5); }
Test module Convention: tests module with cfg(test) #[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() { ... }
}
#[should_panic] Test expects a panic #[test]
#[should_panic(expected = "divide by zero")]
fn test_panic() { divide(1, 0); }
Result-based test Test returns Result (use ? in tests) #[test]
fn test_parse() -> Result<(), Box<dyn Error>> {
let n: i32 = "42".parse()?; assert_eq!(n, 42); Ok(())
}
#[ignore] Skip test unless explicitly requested #[test]
#[ignore]
fn slow_test() { ... }
// Run with: cargo test -- --ignored
Integration tests Tests in tests/ directory // tests/integration_test.rs
use my_crate::add;
#[test]
fn test_add() { assert_eq!(add(2, 3), 5); }
Doc tests Testable examples in documentation /// Adds two numbers.
/// ```
/// assert_eq!(my_crate::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 { a + b }