Cheat sheets π
Everything you keep forgetting, on one page. Print it
(Ctrl/β+P; we styled it for paper) or keep it in a tab.
β‘ Syntax essentials
Variables
let x = 5; // immutable
let mut y = 5; // mutable
y += 1; // ok, y is mut
const MAX: u32 = 100; // constant
let x = x * 2; // shadowing
Functions
fn add(a: i32, b: i32) -> i32 {
a + b // last expression = return
}
fn greet(name: &str) { // no return
println!("Hi, {name}!");
}
Control flow
if x > 5 { ... } else { ... }
let label = if x > 5 { "big" } else { "small" };
for i in 0..10 { ... } // 0 to 9
for item in &my_vec { ... } // borrow items
while x < 100 { ... }
loop { break; } // forever, until break
Printing
println!("plain text");
println!("value: {x}"); // inline var
println!("sum: {}", a + b); // expression
println!("debug: {:?}", vec); // Debug view
println!("pretty: {:#?}", s); // multi-line
eprintln!("to stderr");
π’ Common types
| Type | What it is | Example |
|---|---|---|
i32 | whole numbers (default) | let n: i32 = -7; |
u32 / usize | only-positive numbers / indexes | let len: usize = v.len(); |
f64 | decimals (default float) | let pi = 3.14; |
bool | true / false | let on = true; |
char | one character (any emoji too) | let c = 'π¦'; |
&str | borrowed text view | let s = "hi"; |
String | owned, growable text | let s = String::from("hi"); |
(i32, bool) | tuple: mixed bundle | let t = (5, true); |
[i32; 3] | array: fixed size | let a = [1, 2, 3]; |
Vec<T> | growable list | let v = vec![1, 2, 3]; |
Option<T> | maybe a value | Some(5) / None |
Result<T, E> | success or failure | Ok(5) / Err(e) |
π§ Ownership & borrowing: the whole law
The three ownership rules
- Every value has exactly one owner.
- When the owner goes out of scope, the value is dropped (freed).
- Assigning or passing a heap value moves ownership (unless you
.clone()).
let s1 = String::from("hi");
let s2 = s1; // s1 MOVED into s2
// println!("{s1}"); // β error: s1 is gone
let s3 = s2.clone(); // deep copy, both valid
The two borrowing rules
- Any number of shared borrows
&T, or exactly one mutable borrow&mut T. - No reference may outlive the data it points to.
fn shout(msg: &String) { ... } // reads
fn extend(msg: &mut String) { ... } // edits
let mut s = String::from("hi");
shout(&s); // lend it (read-only)
extend(&mut s); // lend it (writable)
// s is still ours afterwards β
π§© Pattern matching
match
match number {
0 => println!("zero"),
1 | 2 => println!("one or two"),
3..=9 => println!("3 through 9"),
n if n < 0 => println!("negative"),
_ => println!("something else"),
}
Option & Result patterns
match find_user(id) {
Some(user) => greet(&user),
None => println!("no such user"),
}
if let Some(user) = find_user(id) {
greet(&user); // only-one-case shortcut
}
let n: i32 = "42".parse()?; // ? bubbles Err up
let n = "42".parse().unwrap_or(0);
π Collections in practice
Vec: a growable list
let mut v = vec![1, 2, 3];
v.push(4); // add to end
let last = v.pop(); // Option<i32>
let first = v.first(); // Option<&i32>
let n = v[0]; // panics if empty!
let n = v.get(0); // safe: Option
for x in &v { ... } // iterate borrowed
v.len(); v.is_empty(); v.contains(&2);
String: owned text
let mut s = String::from("Hello");
s.push_str(", world"); // append &str
s.push('!'); // append char
let joined = format!("{s} x{n}");
s.to_uppercase(); s.trim(); s.len();
for word in s.split(' ') { ... }
let num: i32 = "42".parse().unwrap();
HashMap: key β value
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert("ferris", 100);
let s = scores.get("ferris"); // Option<&i32>
*scores.entry("crab").or_insert(0) += 1;
for (name, score) in &scores { ... }
Iterator one-liners
let doubled: Vec<i32> =
v.iter().map(|x| x * 2).collect();
let evens: Vec<i32> =
v.iter().filter(|x| *x % 2 == 0)
.cloned().collect();
let total: i32 = v.iter().sum();
let biggest = v.iter().max(); // Option
v.iter().count(); v.iter().rev();
v.iter().enumerate(); // (index, item)
π¨ Error handling
The toolbox
result.unwrap(); // crash on Err π¬
result.expect("msg"); // crash with message
result.unwrap_or(0); // fallback value
result.is_ok(); // just checking
result? // pass Err to caller
option.ok_or("gone")? // Option β Result
Propagating with ?
use std::fs;
fn read_config() -> Result<String, std::io::Error> {
let text = fs::read_to_string("cfg.txt")?;
Ok(text.trim().to_string())
}
// caller decides what a missing file means
π¦ Cargo commands
| Command | What it does |
|---|---|
cargo new my_app | create a new project |
cargo run | compile + run in one step |
cargo check | fast "does it compile?" (no binary) |
cargo build --release | optimized build in target/release |
cargo test | run all tests |
cargo add serde | add a dependency from crates.io |
cargo fmt | auto-format all code |
cargo clippy | the wise linter (catches bad patterns) |
cargo doc --open | build & open docs for your code + deps |
rustup update | update Rust itself |
π§― Borrow-checker survival guide
When the compiler complainsβ¦
- "value moved here" β pass a reference
&valueinstead, or.clone()if you truly need a copy. - "cannot borrow as mutable more than once" β shrink the scope: finish one borrow (often with
{ }blocks) before starting the next. - "cannot borrow as mutable because also borrowed as immutable" β stop reading before you start writing; reorder the code.
- "does not live long enough" β you're returning a reference to something that dies; return an owned
String/Vecinstead.
Beginner rules of thumb
- Functions should usually take
&strinstead ofString, and&[T]instead ofVec<T>. - Reach for
.clone()guilt-free while learning: correct first, optimal later. - Let the compiler drive: read the error, apply its suggestion, repeat.
- When stuck,
cargo clippyoften knows the idiomatic fix.
π¨οΈ Make it paper
Press
Ctrl/β + P. This page has a clean print layout on purpose.
Pin it above your desk like the periodic table of crab.