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

536: 'static Lifetime

Difficulty: 3 Level: Intermediate `'static` means "valid for the entire program." String literals are `'static`. Thread-spawned closures must be `'static`. Understanding when to require it โ€” and when not to โ€” prevents a common class of beginner frustration.

The Problem This Solves

When you spawn a thread or store something in a global, the data must be valid for as long as the program runs. The compiler can't prove a borrowed reference from a local scope survives that long:
fn spawn_worker(data: &str) {
 std::thread::spawn(|| {
     println!("{}", data); // ERROR: data may not live long enough
     // data is borrowed โ€” it could be dropped before the thread finishes
 });
}
The fix is either to own the data (move it into the thread) or require it to be `'static`. The `'static` bound communicates: "I need this to live forever โ€” don't give me a temporary borrow." `T: 'static` doesn't mean `T` is a static variable. It means `T` contains no borrowed references that could expire. An owned `String` satisfies `T: 'static` because it owns all its data.

The Intuition

There are two distinct uses of `'static`: 1. `&'static str` โ€” a reference that truly lives forever (string literals, `static` variables). The data is baked into the binary. 2. `T: 'static` as a bound โ€” means "T owns its data, or its borrows are `'static`." This doesn't mean T lives forever. It means: if T contains references, they're `'static`. An owned `String` satisfies this because it has no references with limited scope. The `'static` bound is really saying "no borrowed data with a limited lifetime" โ€” not "this value will exist forever."

How It Works in Rust

String literals are `'static`:
// Embedded in binary โ€” valid for entire program duration
let s: &'static str = "I will never be freed";
static APP_NAME: &str = "MyApp"; // also &'static str
Thread spawning requires `'static`:
// Works: owned data, moved into thread โ€” satisfies 'static
let data = vec![1, 2, 3];
std::thread::spawn(move || {
 println!("{:?}", data); // data moved โ€” no borrow, safe
});

// Fails: borrowed reference with limited scope
let data = vec![1, 2, 3];
std::thread::spawn(|| {
 println!("{:?}", &data); // ERROR: data doesn't live long enough
});
`T: 'static` bound โ€” what satisfies it:
fn store_globally<T: 'static>(value: T) { /* ... */ }

store_globally(String::from("owned"));  // โœ“ no borrowed refs
store_globally(42i32);                  // โœ“ no refs at all
store_globally(vec!["a", "b"]);         // โœ“ &'static str โ€” fine
store_globally("literal");              // โœ“ &'static str

// &String would fail โ€” it's a borrow with limited scope
let s = String::from("temp");
// store_globally(&s); // ERROR: &s doesn't satisfy 'static
Lazy global initialization:
use std::sync::OnceLock;
static CONFIG: OnceLock<Vec<String>> = OnceLock::new();

fn get_config() -> &'static [String] {
 CONFIG.get_or_init(|| vec!["setting1".to_string()])
 // Returns &'static โ€” valid forever once initialized
}

What This Unlocks

Key Differences

ConceptOCamlRust
String literals`string` values on the heap, GC-managed`&'static str` โ€” embedded in binary, truly immortal
Global state`let` at module level, GC-managed`static` with type annotation; `OnceLock` for lazy initialization
Thread data lifetimeValues kept alive by GC across threadsMust be `'static + Send` โ€” owned or truly immortal
`T: 'static` boundNo equivalent โ€” GC handles all"T owns its data" โ€” no borrowed refs with limited scope
Eternal referencesAll live references are eternal (GC)Only `&'static T` is eternal โ€” all others have scoped lifetimes
//! # 536. 'static Lifetime
//! Program-duration references: literals, statics, and 'static bounds.

/// String literals are &'static str โ€” embedded in binary
static APP_NAME: &str = "MyRustApp";
static VERSION: &str = "1.0.0";
static MAX_CONNECTIONS: usize = 100;

/// Static slice
static ERROR_MESSAGES: &[(u16, &str)] = &[
    (404, "Not Found"),
    (500, "Internal Server Error"),
    (403, "Forbidden"),
    (200, "OK"),
];

fn get_error_msg(code: u16) -> &'static str {
    ERROR_MESSAGES.iter()
        .find(|&&(c, _)| c == code)
        .map(|(_, msg)| *msg)
        .unwrap_or("Unknown Error")
}

/// Requires T: 'static โ€” T must own its data (no borrowed refs)
fn store_globally<T: 'static + std::fmt::Debug>(value: T) {
    // In real code, might store in a global Mutex<Vec<Box<dyn Any>>>
    println!("Storing global: {:?}", value);
}

/// Thread spawning requires 'static โ€” closure must not borrow local data
fn spawn_with_static_data() {
    let data = vec![1, 2, 3]; // owned โ€” satisfies 'static
    let handle = std::thread::spawn(move || {
        // data moved in โ€” no borrowed references
        println!("Thread data: {:?}", data);
        data.iter().sum::<i32>()
    });
    println!("Thread result: {}", handle.join().unwrap());
}

/// What 'static actually means for bounded types
fn demonstrate_static_bound() {
    // String: owns data โ€” satisfies T: 'static
    store_globally(String::from("owned string"));

    // i32: no references at all โ€” satisfies T: 'static
    store_globally(42i32);

    // Vec<String>: owns all data โ€” satisfies T: 'static
    store_globally(vec!["a".to_string(), "b".to_string()]);

    // &'static str โ€” is itself 'static
    store_globally("literal string"); // &'static str satisfies 'static

    // &String would NOT satisfy 'static (unless &'static String)
}

/// Lazy static equivalent using OnceLock
use std::sync::OnceLock;
static GLOBAL_CONFIG: OnceLock<Vec<String>> = OnceLock::new();

fn get_config() -> &'static [String] {
    GLOBAL_CONFIG.get_or_init(|| {
        vec!["setting1".to_string(), "setting2".to_string()]
    })
}

fn main() {
    println!("App: {} v{}", APP_NAME, VERSION);
    println!("Max connections: {}", MAX_CONNECTIONS);
    println!("Error 404: {}", get_error_msg(404));
    println!("Error 418: {}", get_error_msg(418));

    println!("\n=== Thread with 'static data ===");
    spawn_with_static_data();

    println!("\n=== 'static bounds ===");
    demonstrate_static_bound();

    println!("\n=== Lazy static ===");
    let config = get_config();
    println!("Config: {:?}", config);
    let config2 = get_config(); // same allocation
    println!("Config2 (same): {:?}", config2);

    // 'static string references
    let literal: &'static str = "I live forever";
    let also_static = APP_NAME;
    println!("\nLiteral: {}, static: {}", literal, also_static);
}

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

    #[test]
    fn test_static_error_msg() {
        assert_eq!(get_error_msg(404), "Not Found");
        assert_eq!(get_error_msg(200), "OK");
        assert_eq!(get_error_msg(999), "Unknown Error");
    }

    #[test]
    fn test_literal_is_static() {
        let s: &'static str = "test";
        assert_eq!(s, "test");
    }

    #[test]
    fn test_get_config() {
        let config = get_config();
        assert!(!config.is_empty());
    }

    #[test]
    fn test_static_string_bound() {
        // String satisfies 'static
        fn needs_static<T: 'static>(_: T) {}
        needs_static(String::from("hello"));
        needs_static(42i32);
        needs_static(vec![1, 2, 3]);
    }
}
(* Static lifetime analog in OCaml โ€” module-level bindings *)
(* OCaml has no 'static concept โ€” all values are GC-managed *)

(* Module-level bindings are "static" in the sense they live forever *)
let app_name = "MyApp"
let version  = "1.0.0"
let max_connections = 100

(* Lookup table (const equivalent) *)
let error_messages = [
  (404, "Not Found");
  (500, "Internal Server Error");
  (403, "Forbidden");
]

let get_error_msg code =
  List.assoc_opt code error_messages
  |> Option.value ~default:"Unknown Error"

let () =
  Printf.printf "%s v%s\n" app_name version;
  Printf.printf "Max connections: %d\n" max_connections;
  Printf.printf "Error 404: %s\n" (get_error_msg 404);
  Printf.printf "Error 418: %s\n" (get_error_msg 418)