๐Ÿฆ€ 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

537: Lifetime Coercion and Subtyping

Difficulty: 4 Level: Advanced Longer lifetimes are subtypes of shorter ones. A `&'long T` can be used anywhere a `&'short T` is expected โ€” but not the reverse.

The Problem This Solves

Subtyping for lifetimes is what makes `&'static str` usable everywhere a `&str` is expected. Without it, every function that takes `&str` would need a separate overload for `&'static str`. Instead, Rust automatically coerces longer lifetimes to shorter ones at the use site. The confusion is that this feels backwards from what you'd expect: "longer lifetime" is the more specific type. A `&'static str` is a stronger guarantee than a `&'a str` โ€” it commits to living forever. Being a subtype means it can fill in wherever less is required. Understanding this subtyping relationship also explains why `&mut T` can't be coerced the same way โ€” mutable references are invariant, not covariant, and mixing them would break safety.

The Intuition

Lifetime subtyping: `'long: 'short` means "'long outlives 'short," which makes `'long` the subtype. You can always use a subtype where a supertype is expected. Think of it like a precision hierarchy: `'static` is maximally precise (lives forever). Any `'a` is less precise (lives for some scope). A precise tool works wherever a less precise one would โ€” you can always use a laser where a flashlight would do. Coercion is narrowing โ€” the compiler silently shrinks the lifetime annotation to match the expected type. No data moves; it's purely a compile-time label adjustment.

How It Works in Rust

`&'static` coerces to any lifetime:
let literal: &'static str = "I live forever";

// Coercion: &'static str โ†’ &'shorter str
// Happens automatically โ€” no cast needed
let shorter: &str = literal;
fn accepts_any(s: &str) { println!("{}", s); }
accepts_any(literal);  // 'static coerces to whatever 'a the function expects
Explicit coercion in storage:
fn store_long<'long, 'short>(
 storage: &mut Vec<&'short str>,
 item: &'long str,
) where 'long: 'short {  // 'long outlives 'short โ€” coercion is valid
 storage.push(item);  // &'long str coerced to &'short str
}

let mut storage: Vec<&str> = Vec::new();
let permanent = "permanent";  // &'static str
store_long(&mut storage, permanent);  // &'static coerces to &'short
Why `&mut T` is invariant โ€” the danger:
// If &mut T were covariant, this would be possible:
fn bad_coerce<'long, 'short>(r: &mut &'long str, s: &'short str) {
 // *r = s; // If allowed: r now holds &'short str
 // But r was promised to hold &'long str!
 // After 'short expires, *r would be a dangling reference
}
// Rust forbids this โ€” &mut T is invariant to prevent it
Cache demonstrating lifetime storage:
struct Cache<'a> {
 entries: Vec<&'a str>,
}

impl<'a> Cache<'a> {
 // Only accepts refs that live at least as long as 'a
 // &'static str satisfies this (coercion)
 // &'shorter str would fail
 fn insert(&mut self, entry: &'a str) {
     self.entries.push(entry);
 }
}

What This Unlocks

Key Differences

ConceptOCamlRust
Reference validityGC guarantees all live references are validSubtyping: `'long <: 'short` means longer refs can fill shorter slots
String literal usageString literals work anywhere`&'static str` coerces to `&'a str` โ€” automatic at call sites
Mutable reference aliasingGC allows multiple mutable refs (with care)`&mut T` is invariant โ€” no coercion, prevents aliasing bugs
Subtype directionSubtypes typically extend supertypes`'long: 'short` makes longer lifetime the subtype โ€” more specific = subtype
Type coercionsImplicit where safeLifetime coercions (shortening) are implicit; widening is a compile error
//! # 537. Lifetime Coercion and Subtyping
//! Longer lifetimes can be used where shorter ones are required.

/// Accepts a reference valid for at least 'short duration
fn use_briefly<'short>(s: &'short str) {
    println!("Brief use: {}", s);
}

/// Requires long-lived reference
fn store_long<'long: 'short, 'short>(
    storage: &mut Vec<&'short str>,
    item: &'long str, // 'long satisfies 'short requirement
) {
    storage.push(item); // coercion: &'long str -> &'short str
}

/// Demonstrate implicit coercion: longer lifetime used as shorter
fn coercion_demo() {
    let long_lived = String::from("I live a long time");
    let result;
    {
        // short_lived has a shorter scope
        let _short_lived = String::from("I'm short");
        // &long_lived has lifetime 'long > 'short
        // We can use it where 'short is required:
        result = use_briefly(long_lived.as_str()); // coercion happens here
        // result = use_briefly(_short_lived.as_str()); // also works inside scope
    }
    println!("After short scope: {}", long_lived); // still valid
    let _ = result;
}

/// Lifetime narrowing in function calls
fn narrowing_example<'a>(s: &'a str) -> &'a str {
    // Inside here, we can use s as &'shorter str freely
    // The compiler narrows as needed
    &s[..s.len()]
}

/// Collection that stores references โ€” lifetime of stored refs matters
struct Cache<'a> {
    entries: Vec<&'a str>,
}

impl<'a> Cache<'a> {
    fn new() -> Self { Cache { entries: Vec::new() } }

    // Only accepts refs that live at least as long as 'a
    fn insert(&mut self, entry: &'a str) {
        self.entries.push(entry);
    }

    fn first(&self) -> Option<&&'a str> {
        self.entries.first()
    }
}

fn main() {
    coercion_demo();

    // Lifetime narrowing: &'longer used as &'shorter
    let data = String::from("Hello, World!");
    let word = narrowing_example(&data);
    println!("word: {}", word);

    // Cache: entries must outlive the cache
    let s1 = String::from("entry one");
    let s2 = String::from("entry two");

    let mut cache = Cache::new();
    cache.insert(&s1);
    cache.insert(&s2);
    println!("Cache first: {:?}", cache.first());

    // Coercion in assignment
    let long: &'static str = "static string";
    let shorter: &str = long; // &'static coerces to &'any
    println!("Coerced: {}", shorter);

    // Store long-lived refs in short-context
    let mut storage: Vec<&str> = Vec::new();
    let permanent = "permanent data"; // &'static
    store_long(&mut storage, permanent);
    println!("Storage: {:?}", storage);
}

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

    #[test]
    fn test_static_coerces_to_any() {
        let s: &'static str = "hello";
        // &'static satisfies any lifetime requirement
        let _: &str = s; // coercion to 'shorter
        assert_eq!(s, "hello");
    }

    #[test]
    fn test_cache_insert() {
        let a = String::from("a");
        let b = String::from("b");
        let mut cache = Cache::new();
        cache.insert(&a);
        cache.insert(&b);
        assert_eq!(cache.entries.len(), 2);
    }

    #[test]
    fn test_narrowing() {
        let s = String::from("test");
        let r = narrowing_example(&s);
        assert_eq!(r, "test");
    }
}
(* Lifetime subtyping is implicit in OCaml โ€” GC handles it *)
(* Demonstrating the concept with value semantics *)
let use_short_lived s =
  Printf.printf "Short-lived: %s\n" s

let use_long_lived s =
  Printf.printf "Long-lived: %s\n" s

let () =
  (* In OCaml, any string can be used where a "shorter-lived" one is needed *)
  let long_lived = "This string lives long" in
  use_short_lived long_lived;   (* using longer-lived value in shorter context *)
  use_long_lived long_lived;

  (* The concept: you can always use something that lives LONGER *)
  let create_and_use () =
    let s = "created here" in
    use_short_lived s;  (* s goes out of scope after this block *)
    s (* but OCaml keeps it alive via GC *)
  in
  let result = create_and_use () in
  Printf.printf "Still valid: %s\n" result