Variables & Types
let x = 5;Immutable binding — cannot reassignlet mut x = 5;Mutable binding — can reassignconst MAX: u32 = 100;Constant — must have type annotation, evaluated at compile timestatic GREETING: &str = "hello";Static — lives for entire program durationlet x: i32 = 42;Explicit type annotationi8 / i16 / i32 / i64 / i128 / isizeSigned integersu8 / u16 / u32 / u64 / u128 / usizeUnsigned integers (usize used for indexing)f32 / f64Floating point (f64 is default)booltrue or falsecharUnicode scalar value — single quotes: 'A'(i32, &str)Tuple typelet tup = (1, "hi"); tup.0 / tup.1Tuple access by indexlet (a, b) = tup;Destructure tuple[i32; 5]Fixed-size array typelet arr = [1, 2, 3, 4, 5]; arr[0]Array literal and index accesslet arr = [0; 5];Array of 5 zeroslet x = { let y = 3; y + 1 };Block expression — evaluates to last expression (no semicolon)Strings
&strString slice — immutable reference to UTF-8 data, usually in binaryStringOwned, heap-allocated, growable UTF-8 string"hello"String literal — type is &strString::from("hello")Create owned String from literal"hello".to_string()Convert &str to Strings.as_str() / &sBorrow a String as &strs.len()Byte lengths.is_empty()True if length is 0s.contains("sub")True if substring founds.starts_with("pre") / s.ends_with("suf")Check prefix / suffixs.find("sub")Return Options.replace("a", "b")Replace all occurrences — returns new Strings.to_uppercase() / s.to_lowercase()Change case — returns new Strings.trim() / s.trim_start() / s.trim_end()Strip whitespace — returns &str slices.split(",").collect::<Vec<_>>()Split on delimiter, collect into Vecs.chars().count()Number of Unicode scalar valuess.chars().nth(2)Nth character as Options1 + &s2Concatenate — moves s1, borrows s2format!("{} {}", a, b)Format macro — returns owned String42.to_string()Convert number to String"42".parse::<i32>()Parse string to typed value — returns ResultCollections
let v: Vec<i32> = Vec::new();Create empty Veclet v = vec![1, 2, 3];Vec literal via macrov.push(4)Append elementv.pop()Remove and return last element as Optionv.len() / v.is_empty()Length / empty checkv[0] / v.get(0)Index access (panics if OOB) / safe access returning Optionv.iter()Iterate by reference (&T)v.iter_mut()Iterate by mutable reference (&mut T)v.into_iter()Consuming iterator — takes ownership (T)v.iter().map(|x| x * 2).collect::<Vec<_>>()Transform and collectv.iter().filter(|x| **x > 2).collect::<Vec<_>>()Filter and collectv.iter().fold(0, |acc, x| acc + x)Fold to single valuev.iter().sum::<i32>() / v.iter().product::<i32>()Sum / productv.iter().any(|x| *x > 3) / v.iter().all(|x| *x > 0)Any / all match predicatev.sort() / v.sort_by(|a, b| a.cmp(b))Sort in place / with comparatorv.dedup()Remove consecutive duplicates (sort first for full dedup)v.retain(|x| *x > 0)Keep only elements matching predicateuse std::collections::HashMap;Import HashMaplet mut m = HashMap::new();Create empty HashMapm.insert("key", 1);Insert key-value pairm.get("key")Returns Option<&V>m.contains_key("key")True if key existsm.remove("key")Remove key, returns Optionm.entry("key").or_insert(0)Get or insert default valueuse std::collections::HashSet;Import HashSetControl Flow
if x > 0 { } else if x < 0 { } else { }if/else — no parentheses; braces requiredlet y = if x > 0 { 1 } else { -1 };if as expression — both arms must return same typeloop { ... break value; }Infinite loop — can return a value via breakwhile x > 0 { }Loop while condition is truefor x in collection { }For loop — takes ownership or borrows depending on iteratorfor x in &v { }Iterate by referencefor (i, x) in v.iter().enumerate() { }Iterate with indexfor x in 0..10 { } / 0..=10Exclusive range / inclusive rangebreak / continueExit loop / skip to next iterationmatch x {
1 => "one",
2 | 3 => "two or three",
4..=9 => "four to nine",
_ => "other",
}match — exhaustive, no fallthroughmatch x { Some(v) => v, None => 0 }match on enum variantsif let Some(v) = opt { }if let — match one pattern, ignore restwhile let Some(v) = iter.next() { }while let — loop while pattern matcheslet Some(v) = opt else { return; };let-else — bind or run diverging else block (Rust 1.65+)Functions
fn add(a: i32, b: i32) -> i32 { a + b }Function — last expression without semicolon is the return valuefn greet() { }Function returning unit ()fn never() -> ! { panic!("!") }! — never type, function never returnsreturn value;Explicit early return|x| x * 2Closure — captures environment|x: i32| -> i32 { x * 2 }Closure with explicit typesmove |x| x + offsetmove closure — takes ownership of captured variablesfn apply(f: impl Fn(i32) -> i32, x: i32) -> i32Accept closure as argument with impl Traitfn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32Same with generic boundfn make_adder(x: i32) -> impl Fn(i32) -> i32 { move |y| x + y }Return a closurefn largest<T: PartialOrd>(list: &[T]) -> &TGeneric function with trait boundfn first<T>(list: &[T]) -> Option<&T> { list.get(0) }Return reference with lifetime tied to inputStructs & Enums
struct Point { x: f64, y: f64 }Named-field structstruct Pair(i32, i32);Tuple structstruct Marker;Unit struct — no fieldslet p = Point { x: 1.0, y: 2.0 };Struct literallet p2 = Point { x: 3.0, ..p };Struct update — copy remaining fields from pp.x / p.yField accesslet Point { x, y } = p;Destructure structimpl Point { fn distance(&self) -> f64 { } }impl block — attach methods to structfn new(x: f64, y: f64) -> Self { Point { x, y } }Associated function (constructor convention)&self / &mut self / selfImmutable borrow / mutable borrow / take ownership#[derive(Debug, Clone, PartialEq)]Derive common traits automaticallyenum Direction { North, South, East, West }Simple enumenum Shape { Circle(f64), Rect(f64, f64) }Enum with dataenum Message { Quit, Move { x: i32, y: i32 }, Write(String) }Enum with varied data per variantOption<T>Built-in enum: Some(T) or None — no null in RustResult<T, E>Built-in enum: Ok(T) or Err(E) — for fallible operationsOwnership & Borrowing
let s1 = String::from("hi"); let s2 = s1;Move — s1 is no longer valid; heap data transferred to s2let s2 = s1.clone();Deep copy — both s1 and s2 are validlet x = 5; let y = x;Copy — primitives implement Copy, no movefn take(s: String) { } // s dropped herePassing to function moves ownershipfn borrow(s: &String) { }Shared reference — borrow without taking ownershipfn mutate(s: &mut String) { }Mutable reference — exclusive, no other borrows active&TShared reference — many readers, no writers&mut TExclusive reference — one writer, no readersfn longest<'a>(x: &'a str, y: &'a str) -> &'a strLifetime annotation — output lives as long as shorter of x, ystruct Excerpt<'a> { text: &'a str }Struct holding a reference needs lifetime parameterBox<T>Heap-allocated value — single ownerRc<T>Reference-counted pointer — multiple owners, single threadArc<T>Atomic reference-counted pointer — multiple owners, thread-safeRefCell<T>Interior mutability — borrow rules checked at runtimeRc<RefCell<T>>Common pattern for shared mutable state in single-threaded codeError Handling
Result<T, E>Ok(T) on success, Err(E) on failureOption<T>Some(T) if present, None if absentresult?? operator — return Err early if Err, unwrap if Okopt?? operator on Option — return None early if Noneresult.unwrap()Get Ok value — panics if Errresult.expect("msg")Like unwrap but panics with a custom messageresult.unwrap_or(0)Get Ok value or defaultresult.unwrap_or_else(|e| handle(e))Get Ok value or compute default from errorresult.is_ok() / result.is_err()Check result variant without consumingresult.map(|v| v * 2)Transform Ok value, pass Err throughresult.map_err(|e| MyError(e))Transform Err value, pass Ok throughresult.and_then(|v| fallible(v))Chain fallible operations — flatMapopt.unwrap_or_default()Get Some value or type defaultopt.map(|v| v * 2)Transform Some value, pass None throughopt.ok_or(MyError)Convert Option to Result#[derive(Debug)]\nenum AppError { NotFound, ParseFailed(String) }Custom error enumimpl std::error::Error for AppError {}Implement the Error trait for compatibilitypanic!("unexpected state")Unrecoverable error — unwinds stackTraits
trait Summary { fn summarise(&self) -> String; }Define a traittrait Greet { fn greet(&self) -> String { String::from("Hello") } }Trait with default method implementationimpl Summary for Article { fn summarise(&self) -> String { ... } }Implement a trait for a typefn notify(item: &impl Summary)impl Trait in parameter positionfn notify<T: Summary>(item: &T)Generic with trait bound — same as abovefn notify<T: Summary + Display>(item: &T)Multiple trait boundsfn notify<T>(item: &T) where T: Summary + Displaywhere clause — cleaner for complex boundsfn returns_summary() -> impl SummaryReturn a type that implements a traitBox<dyn Summary>Trait object — dynamic dispatch, heap-allocatedDisplay / Debug / Clone / Copy / PartialEq / Eq / Hash / OrdCommon standard library traitsFrom<T> / Into<T>Infallible conversion — impl From, get Into for freeTryFrom<T> / TryInto<T>Fallible conversion — returns ResultIterator — fn next(&mut self) -> Option<Self::Item>Core iterator trait — implement next, get map/filter/etc for freeimpl Iterator for Counter { type Item = u32; ... }Custom iterator with associated type