ExamplesBy LevelBy TopicLearning Paths
560 Intermediate

Lifetime Cheatsheet

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Lifetime Cheatsheet" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Lifetime annotations in Rust are a powerful but syntactically noisy feature. Key difference from OCaml: 1. **Annotation presence**: Rust requires lifetime annotations in many signatures; OCaml requires none — the entire domain of knowledge captured here is Rust

Tutorial

The Problem

Lifetime annotations in Rust are a powerful but syntactically noisy feature. After learning each individual rule in isolation, programmers often struggle to apply them quickly when reading or writing real code. A cheatsheet consolidates the most common patterns — elision, structs with lifetimes, impl blocks, static lifetimes, HRTB, and subtyping — into a single reference with expanded forms and their practical interpretations.

🎯 Learning Outcomes

  • • Quick recall of all major lifetime annotation patterns in one place
  • • How to read elided lifetimes and mentally expand them to explicit forms
  • • Common struct, impl, and trait patterns with lifetimes side-by-side
  • • When each pattern is appropriate and what it communicates about the API
  • • Lifetime-related terminology: region, covariance, subtyping, HRTB, static
  • Code Example

    #![allow(clippy::all)]
    //! Lifetime Cheatsheet
    //!
    //! Quick reference for common lifetime patterns.
    
    // 'a: Lifetime parameter
    // &'a T: Reference valid for 'a
    // &'static T: Reference valid forever
    // T: 'a: T outlives 'a
    
    /// Elision: one input → output.
    pub fn trim(s: &str) -> &str {
        s.trim()
    }
    
    /// Explicit: multiple inputs.
    pub fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
        if a.len() >= b.len() {
            a
        } else {
            b
        }
    }
    
    /// Struct with lifetime.
    pub struct View<'a> {
        pub data: &'a str,
    }
    
    /// Impl with lifetime.
    impl<'a> View<'a> {
        pub fn new(data: &'a str) -> Self {
            View { data }
        }
    }
    
    /// Static lifetime.
    pub fn get_static() -> &'static str {
        "static"
    }
    
    /// Bound: T outlives 'a.
    pub fn bound<'a, T: 'a>(_t: &'a T) {}
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_trim() {
            assert_eq!(trim("  x  "), "x");
        }
    
        #[test]
        fn test_longer() {
            assert_eq!(longer("ab", "abc"), "abc");
        }
    
        #[test]
        fn test_view() {
            let v = View::new("test");
            assert_eq!(v.data, "test");
        }
    
        #[test]
        fn test_static() {
            assert_eq!(get_static(), "static");
        }
    }

    Key Differences

  • Annotation presence: Rust requires lifetime annotations in many signatures; OCaml requires none — the entire domain of knowledge captured here is Rust-specific.
  • Learning curve: Lifetimes are the most commonly cited Rust learning challenge; OCaml's GC model is simpler to learn but less powerful for systems programming.
  • Cheatsheet necessity: Rust programmers frequently consult lifetime rules; OCaml programmers rarely need to look up memory management syntax.
  • Runtime vs compile-time: Rust's lifetime system moves memory safety guarantees to compile time; OCaml's GC provides them at runtime — both are correct, with different performance profiles.
  • OCaml Approach

    OCaml has no lifetime syntax — the cheatsheet concept does not apply. The equivalent reference for OCaml would cover GC semantics, weak references, and the Gc module rather than lifetime annotations.

    (* OCaml "lifetime cheatsheet": everything is GC-managed
       - ref t: mutable reference, always valid while reachable
       - Weak.t: non-retaining reference, becomes None after GC
       - module Gc: control GC behavior
    *)
    

    Full Source

    #![allow(clippy::all)]
    //! Lifetime Cheatsheet
    //!
    //! Quick reference for common lifetime patterns.
    
    // 'a: Lifetime parameter
    // &'a T: Reference valid for 'a
    // &'static T: Reference valid forever
    // T: 'a: T outlives 'a
    
    /// Elision: one input → output.
    pub fn trim(s: &str) -> &str {
        s.trim()
    }
    
    /// Explicit: multiple inputs.
    pub fn longer<'a>(a: &'a str, b: &'a str) -> &'a str {
        if a.len() >= b.len() {
            a
        } else {
            b
        }
    }
    
    /// Struct with lifetime.
    pub struct View<'a> {
        pub data: &'a str,
    }
    
    /// Impl with lifetime.
    impl<'a> View<'a> {
        pub fn new(data: &'a str) -> Self {
            View { data }
        }
    }
    
    /// Static lifetime.
    pub fn get_static() -> &'static str {
        "static"
    }
    
    /// Bound: T outlives 'a.
    pub fn bound<'a, T: 'a>(_t: &'a T) {}
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_trim() {
            assert_eq!(trim("  x  "), "x");
        }
    
        #[test]
        fn test_longer() {
            assert_eq!(longer("ab", "abc"), "abc");
        }
    
        #[test]
        fn test_view() {
            let v = View::new("test");
            assert_eq!(v.data, "test");
        }
    
        #[test]
        fn test_static() {
            assert_eq!(get_static(), "static");
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_trim() {
            assert_eq!(trim("  x  "), "x");
        }
    
        #[test]
        fn test_longer() {
            assert_eq!(longer("ab", "abc"), "abc");
        }
    
        #[test]
        fn test_view() {
            let v = View::new("test");
            assert_eq!(v.data, "test");
        }
    
        #[test]
        fn test_static() {
            assert_eq!(get_static(), "static");
        }
    }

    Deep Comparison

    OCaml vs Rust: lifetime cheatsheet

    See example.rs and example.ml for implementations.

    Exercises

  • Pattern classification: Take five functions from previous examples and classify each lifetime parameter by its role: input constraint, output source, subtyping, or HRTB.
  • Expand elided forms: Write the fully-explicit lifetime-annotated forms of fn process(s: &str) -> &str, fn combine(a: &str, b: &str) -> String, and a struct method returning &str.
  • Custom cheatsheet: Add two more patterns not covered in the source: (a) a function with a where T: 'a bound, and (b) a function with an impl Trait + 'a return type — explain each in a comment.
  • Open Source Repos