Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Syllabus and Snippets

This chapter is a compact Rust review sheet. The first section is only the syllabus. The later sections are recall snippets meant to make the basics easy to retrieve under interview pressure.

Syllabus

  1. Variables, mutability, shadowing, primitive types, tuples, arrays, and slices.
  2. Ownership: move, copy, drop, scope, stack vs heap.
  3. Borrowing: &T, &mut T, aliasing rules, and borrow conflicts.
  4. References and strings: String, &str, slices, conversions.
  5. Structs, enums, Option, Result, and pattern matching.
  6. Functions, methods, impl, associated functions, modules, and visibility.
  7. Generics, traits, trait bounds, where, impl Trait, and dyn Trait.
  8. Lifetimes: why they exist, elision, and borrowed return values.
  9. Collections: Vec, HashMap, String, iteration, and ownership during iteration.
  10. Iterators: iter, iter_mut, into_iter, map, filter, and collect.
  11. Error handling: Result, ?, custom errors, propagation, and avoiding unnecessary unwrap.
  12. Smart pointers and interior mutability: Box, Rc, Arc, RefCell, Mutex.
  13. Concurrency: threads, channels, Arc<Mutex<T>>, Send, and Sync.
  14. Async Rust: async/.await, futures as state machines, tokio::spawn, select!, and blocking hazards.
  15. Unsafe Rust and FFI: raw pointers, unsafe, invariants, and extern "C".

Core Snippets

Variables, mutability, shadowing

fn main() {
    let x = 5;
    let mut y = 10;
    y += x;

    let y = y.to_string();
    println!("{x} {y}");
}

Tuples, arrays, slices

fn main() {
    let pair = (42, "rust");
    let nums = [1, 2, 3, 4];
    let part: &[i32] = &nums[1..3];

    println!("{} {}", pair.0, pair.1);
    println!("{part:?}");
}

Ownership and move

fn takes_ownership(s: String) {
    println!("{s}");
}

fn main() {
    let s = String::from("hello");
    takes_ownership(s);
    // s is moved here
}

Copy type vs moved type

fn main() {
    let a = 5;
    let b = a;

    let s1 = String::from("hi");
    let s2 = s1;

    println!("{a} {b}");
    // println!("{s1}"); // moved
    println!("{s2}");
}

Borrowing

fn len_of(s: &String) -> usize {
    s.len()
}

fn main() {
    let s = String::from("hello");
    let n = len_of(&s);
    println!("{s} {n}");
}

Mutable borrowing

fn append_world(s: &mut String) {
    s.push_str(" world");
}

fn main() {
    let mut s = String::from("hello");
    append_world(&mut s);
    println!("{s}");
}

String vs &str

fn greet(name: &str) {
    println!("hello, {name}");
}

fn main() {
    let owned = String::from("bobby");
    let borrowed: &str = &owned;

    greet(&owned);
    greet(borrowed);
    greet("rust");
}

Data Modeling Snippets

Struct and impl

#![allow(unused)]
fn main() {
struct User {
    name: String,
    active: bool,
}

impl User {
    fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            active: true,
        }
    }

    fn deactivate(&mut self) {
        self.active = false;
    }
}
}

Enum and match

#![allow(unused)]
fn main() {
enum State {
    Ready,
    Running(u32),
    Failed(String),
}

fn describe(state: State) -> String {
    match state {
        State::Ready => "ready".to_string(),
        State::Running(pid) => format!("running: {pid}"),
        State::Failed(msg) => format!("failed: {msg}"),
    }
}
}

Option

fn first_char(s: &str) -> Option<char> {
    s.chars().next()
}

fn main() {
    match first_char("rust") {
        Some(c) => println!("{c}"),
        None => println!("empty"),
    }
}

Result

#![allow(unused)]
fn main() {
fn parse_port(input: &str) -> Result<u16, std::num::ParseIntError> {
    input.parse::<u16>()
}
}

if let and while let

fn main() {
    let maybe = Some(10);

    if let Some(v) = maybe {
        println!("{v}");
    }

    let mut stack = vec![1, 2, 3];
    while let Some(v) = stack.pop() {
        println!("{v}");
    }
}

Functions, Modules, Traits, Generics

Associated function and method

#![allow(unused)]
fn main() {
struct Counter(u32);

impl Counter {
    fn new() -> Self {
        Self(0)
    }

    fn inc(&mut self) {
        self.0 += 1;
    }
}
}

Module and visibility

mod math {
    pub fn add(a: i32, b: i32) -> i32 {
        a + b
    }
}

fn main() {
    println!("{}", math::add(2, 3));
}

Trait and impl

#![allow(unused)]
fn main() {
trait Describe {
    fn describe(&self) -> String;
}

struct Job {
    id: u64,
}

impl Describe for Job {
    fn describe(&self) -> String {
        format!("job: {}", self.id)
    }
}
}

Generic function with trait bound

#![allow(unused)]
fn main() {
fn print_twice<T: std::fmt::Display>(value: T) {
    println!("{value}");
    println!("{value}");
}
}

where clause

#![allow(unused)]
fn main() {
fn pair_to_string<T, U>(a: T, b: U) -> String
where
    T: std::fmt::Display,
    U: std::fmt::Display,
{
    format!("{a}:{b}")
}
}

impl Trait and dyn Trait

#![allow(unused)]
fn main() {
fn make_iter() -> impl Iterator<Item = i32> {
    vec![1, 2, 3].into_iter()
}

fn describe_dyn(x: &dyn std::fmt::Display) {
    println!("{x}");
}
}

Lifetime Snippets

Borrowed return value

#![allow(unused)]
fn main() {
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() >= b.len() { a } else { b }
}
}

Lifetime in struct

#![allow(unused)]
fn main() {
struct View<'a> {
    text: &'a str,
}
}

Collections and Iterator Snippets

Vec

fn main() {
    let mut nums = vec![1, 2, 3];
    nums.push(4);

    for n in &nums {
        println!("{n}");
    }
}

HashMap

use std::collections::HashMap;

fn main() {
    let mut counts = HashMap::new();
    counts.insert("rust", 2);
    counts.entry("tokio").or_insert(1);
}

iter, iter_mut, into_iter

fn main() {
    let mut nums = vec![1, 2, 3];

    for n in nums.iter() {
        println!("{n}");
    }

    for n in nums.iter_mut() {
        *n *= 2;
    }

    for n in nums.into_iter() {
        println!("{n}");
    }
}

Iterator pipeline

fn main() {
    let nums = vec![1, 2, 3, 4, 5, 6];

    let evens: Vec<i32> = nums
        .into_iter()
        .filter(|n| n % 2 == 0)
        .map(|n| n * 10)
        .collect();

    println!("{evens:?}");
}

Error Handling Snippets

? operator

#![allow(unused)]
fn main() {
use std::fs;
use std::io;

fn read_config() -> Result<String, io::Error> {
    let contents = fs::read_to_string("config.toml")?;
    Ok(contents)
}
}

Custom error enum

#![allow(unused)]
fn main() {
#[derive(Debug)]
enum AppError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
}

impl From<std::io::Error> for AppError {
    fn from(err: std::io::Error) -> Self {
        AppError::Io(err)
    }
}

impl From<std::num::ParseIntError> for AppError {
    fn from(err: std::num::ParseIntError) -> Self {
        AppError::Parse(err)
    }
}
}

Smart Pointer and Concurrency Snippets

Box

#![allow(unused)]
fn main() {
enum List {
    Cons(i32, Box<List>),
    Nil,
}
}

Rc

use std::rc::Rc;

fn main() {
    let a = Rc::new(String::from("shared"));
    let b = Rc::clone(&a);

    println!("{a} {b}");
}

Arc<Mutex<T>>

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let count = Arc::new(Mutex::new(0));
    let mut handles = Vec::new();

    for _ in 0..4 {
        let count = Arc::clone(&count);
        handles.push(thread::spawn(move || {
            let mut guard = count.lock().unwrap();
            *guard += 1;
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("{}", *count.lock().unwrap());
}

Channel

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        tx.send(String::from("hello")).unwrap();
    });

    println!("{}", rx.recv().unwrap());
}

Send and Sync mental model

#![allow(unused)]
fn main() {
// Send: value can move to another thread.
// Sync: shared reference can be used from another thread.
}

Recall Checklist

  • Can I explain move vs borrow vs mutable borrow without hesitation?
  • Can I explain String vs &str?
  • Can I model absence with Option and failure with Result?
  • Can I write a struct, enum, impl, and match from memory?
  • Can I explain trait bounds and when to use dyn Trait?
  • Can I explain what lifetimes are protecting?
  • Can I use Vec, HashMap, and iterators comfortably?
  • Can I explain Arc<Mutex<T>>, Send, and Sync?
  • Can I explain the basics well enough before moving on to async and unsafe?