๐Ÿฆ€ Functional Rust
๐ŸŽฌ Rust Ownership in 30 seconds Visual walkthrough of ownership, moves, and automatic memory management.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Each value in Rust has exactly one owner โ€” when the owner goes out of scope, the value is dropped

โ€ข Assignment moves ownership by default; the original binding becomes invalid

โ€ข Borrowing (&T / &mut T) lets you reference data without taking ownership

โ€ข The compiler enforces: many shared references OR one mutable reference, never both

โ€ข No garbage collector needed โ€” memory is freed deterministically at scope exit

560: Lifetime Annotation Cheatsheet

Difficulty: 3 Level: Intermediate All common lifetime annotation patterns in one place. Use this as a reference when you need the syntax and can't remember which form goes where.

The Problem This Solves

Lifetime annotations appear in many different syntactic positions in Rust โ€” function signatures, struct definitions, `impl` blocks, trait bounds, closures, and type positions. Each position has its own rules and idioms. Having them all in one place prevents the "I know what I need but forgot the syntax" slowdown. This cheatsheet covers the 90% of cases you'll encounter in real Rust code, with brief notes on when to use each form.

The Intuition

Lifetime annotations follow a consistent pattern: declare the lifetime parameter in `<>` at the item level, then use it on references within that item. The label `'a` is just a name โ€” choose any name, though `'a`, `'b`, `'static`, and `'_` are idiomatic.

How It Works in Rust

Function signatures:
// Most common: elided โ€” compiler infers
fn first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") }

// Explicit: two inputs, one output โ€” must annotate which source
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
 if x.len() >= y.len() { x } else { y }
}

// Independent lifetimes: output from x only, y doesn't constrain result
fn first_of<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x }

// Static: return lives forever (embedded in binary)
fn greeting() -> &'static str { "Hello!" }

// Anonymous: explicit elision โ€” '_ means "infer it"
fn get_first(v: &[i32]) -> Option<&'_ i32> { v.first() }
Struct definitions:
// Struct with one borrowed field
struct StrWrapper<'a> { value: &'a str }

// Multiple independent borrowed fields
struct PairRef<'a, 'b> { first: &'a str, second: &'b str }

// Generic with lifetime
struct Container<'a, T> { items: &'a [T], label: &'a str }
impl blocks:
impl<'a> StrWrapper<'a> {
 fn new(s: &'a str) -> Self { StrWrapper { value: s } }
 
 // Rule 3 elision: &self method โ†’ return tied to self
 fn get(&self) -> &str { self.value }
 
 // Explicit: tied to 'a (data lifetime), not self's borrow
 fn get_explicit(&self) -> &'a str { self.value }
}
Trait bounds and where clauses:
// T must not contain borrows shorter than 'static
fn store<T: 'static>(value: T) { /* ... */ }

// T must outlive 'a (T can contain refs, but they must be >= 'a)
fn use_ref<'a, T: 'a>(r: &'a T) -> &'a T { r }

// 'a outlives 'b
fn constrained<'a: 'b, 'b>(x: &'a str, _y: &'b str) -> &'b str { x }
Higher-ranked trait bounds:
// F must work for ANY lifetime โ€” not just one specific 'a
fn apply<F>(s: &str, f: F) -> usize
where F: for<'a> Fn(&'a str) -> usize
{ f(s) }
Closures:
// Closure captures &'a str โ€” valid for 'a
fn make_logger<'a>(prefix: &'a str) -> impl Fn(&str) -> String + 'a {
 move |s| format!("{}: {}", prefix, s)
}
dyn Trait:
// Default: 'static (owns all data)
fn store_renderer(r: Box<dyn Renderer>) { /* ... */ }

// Explicit lifetime: can borrow from 'a
fn use_renderer<'a>(r: Box<dyn Renderer + 'a>, data: &'a str) { /* ... */ }

What This Unlocks

Key Differences

AnnotationMeaningWhen to use
`&str` (elided)Compiler infers via elision rules90% of cases โ€” single input, `&self` method
`&'a str`Explicitly named lifetimeMultiple refs where output source is ambiguous
`&'static str`Lives for entire programString literals, `static` variables
`&'_ str`Explicit elision markerType positions where you want to be explicit
`T: 'static`T owns its dataThread spawning, global storage, `Box<dyn Trait>`
`T: 'a`T's refs live >= 'aGeneric functions that store refs
`'a: 'b`'a outlives 'bLifetime ordering constraints
`for<'a>`Works for any lifetimeGeneric callbacks, trait objects with reference arguments
//! # 560. Lifetime Annotation Cheatsheet
//! Quick reference for all common lifetime annotation patterns.

// =============================================================================
// 1. FUNCTION SIGNATURES
// =============================================================================

// Elided (most common โ€” let compiler infer)
fn first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") }

// Explicit (when multiple inputs, output tied to one)
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() >= y.len() { x } else { y }
}

// Different input lifetimes, output from first
fn first_of<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x }

// Static lifetime
fn get_greeting() -> &'static str { "Hello, World!" }

// =============================================================================
// 2. STRUCT DEFINITIONS
// =============================================================================

// Struct borrowing a string
struct StrWrapper<'a> {
    value: &'a str,
}

// Struct with multiple lifetime params
struct PairRef<'a, 'b> {
    first: &'a str,
    second: &'b str,
}

// Generic struct with lifetime
struct Container<'a, T> {
    items: &'a [T],
    label: &'a str,
}

// =============================================================================
// 3. IMPL BLOCKS
// =============================================================================

impl<'a> StrWrapper<'a> {
    fn new(s: &'a str) -> Self { StrWrapper { value: s } }
    fn get(&self) -> &str { self.value } // rule 3: tied to &self
    fn get_explicit(&self) -> &'a str { self.value } // explicit โ€” tied to 'a
}

impl<'a, 'b> PairRef<'a, 'b> {
    fn new(first: &'a str, second: &'b str) -> Self { PairRef { first, second } }
    fn first(&self) -> &'a str { self.first }
    fn second(&self) -> &'b str { self.second }
}

// =============================================================================
// 4. TRAIT DEFINITIONS AND IMPLEMENTATIONS
// =============================================================================

trait Borrow<'a> {
    fn borrow(&'a self) -> &'a str;
}

struct Named { name: String }
impl<'a> Borrow<'a> for Named {
    fn borrow(&'a self) -> &'a str { &self.name }
}

// =============================================================================
// 5. HIGHER-RANKED TRAIT BOUNDS (for<'a>)
// =============================================================================

fn apply_to_str<F>(s: &str, f: F) -> usize
where
    F: for<'a> Fn(&'a str) -> usize, // works for any lifetime
{
    f(s)
}

// =============================================================================
// 6. CLOSURES WITH LIFETIMES
// =============================================================================

fn make_prefix_fn<'a>(prefix: &'a str) -> impl Fn(&str) -> String + 'a {
    move |s| format!("{}: {}", prefix, s)
}

// =============================================================================
// 7. LIFETIME BOUNDS ON TYPES
// =============================================================================

// T must not contain references shorter than 'static
fn store<T: 'static>(value: T) -> Box<dyn std::any::Any> {
    Box::new(value)
}

// T must outlive 'a
fn use_ref<'a, T: 'a>(r: &'a T) -> &'a T { r }

// =============================================================================
// 8. ANONYMOUS LIFETIMES
// =============================================================================

// '_ means "infer the lifetime"
fn get_first(v: &[i32]) -> Option<&'_ i32> { v.first() } // '_ is explicit elision

fn main() {
    // 1. Functions
    println!("first_word: {}", first_word("hello world"));
    println!("longest: {}", longest("hi", "hello"));
    println!("greeting: {}", get_greeting());

    // 2. Structs
    let s = String::from("wrapper content");
    let w = StrWrapper::new(&s);
    println!("wrapper.get(): {}", w.get());

    let a = String::from("alpha");
    let b = String::from("beta");
    let pair = PairRef::new(&a, &b);
    println!("pair: {} / {}", pair.first(), pair.second());

    // 3. Container
    let items = vec![1, 2, 3, 4, 5];
    let c = Container { items: &items, label: "numbers" };
    println!("container '{}': {:?}", c.label, c.items);

    // 4. Trait
    let named = Named { name: "Alice".to_string() };
    println!("named: {}", named.borrow());

    // 5. HRTB
    let len = apply_to_str("hello world", |s| s.len());
    println!("len via HRTB: {}", len);

    // 6. Closure with lifetime
    let prefix = String::from("INFO");
    let log = make_prefix_fn(&prefix);
    println!("{}", log("server started"));

    // 7. Lifetime bounds
    let boxed = store(42i32);
    println!("stored: {:?}", boxed.downcast_ref::<i32>());

    // 8. Anonymous
    let v = vec![10, 20, 30];
    println!("first: {:?}", get_first(&v));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_first_word() { assert_eq!(first_word("hello world"), "hello"); }

    #[test]
    fn test_longest() {
        assert_eq!(longest("abc", "ab"), "abc");
        assert_eq!(longest("x", "xyz"), "xyz");
    }

    #[test]
    fn test_str_wrapper() {
        let s = String::from("test");
        let w = StrWrapper::new(&s);
        assert_eq!(w.get(), "test");
        assert_eq!(w.get_explicit(), "test");
    }

    #[test]
    fn test_pair_ref() {
        let a = String::from("a");
        let b = String::from("b");
        let p = PairRef::new(&a, &b);
        assert_eq!(p.first(), "a");
        assert_eq!(p.second(), "b");
    }

    #[test]
    fn test_hrtb() {
        let count = apply_to_str("a b c d", |s| s.split_whitespace().count());
        assert_eq!(count, 4);
    }

    #[test]
    fn test_closure_lifetime() {
        let p = String::from("LOG");
        let f = make_prefix_fn(&p);
        assert_eq!(f("msg"), "LOG: msg");
    }
}
(* Lifetime cheatsheet in OCaml -- none needed! *)
(* This file shows the patterns OCaml doesn't need annotations for *)

(* 1. Function returning borrowed value *)
let identity x = x

(* 2. Struct with "borrowed" fields -- OCaml owns them *)
type config = { host: string; port: int }

(* 3. Multiple input references *)
let prefer_first a _b = a

(* 4. Static values *)
let static_name = "constant"

(* 5. Generic function *)
let apply f x = f x

(* 6. Nested references *)
let deref_and_use r = !r

let () =
  let cfg = { host = "localhost"; port = 8080 } in
  Printf.printf "host: %s\n" cfg.host;
  Printf.printf "prefer_first: %s\n" (prefer_first "a" "b");
  Printf.printf "apply double 5: %d\n" (apply (fun x -> x * 2) 5);
  let r = ref 42 in
  Printf.printf "deref: %d\n" (deref_and_use r)