Advanced Lifetimes and Borrowing

Higher-Ranked Trait Bounds

A Higher-Ranked Trait Bound (HRTB) is a trait bound—written for<'a> F: Trait<'a>—that requires a type F to satisfy a trait for every possible lifetime 'a chosen inside the function body, rather than for a single fixed lifetime chosen by the caller before the function is called.

Early-Bound Lifetimes vs. Higher-Ranked (Late-Bound) Lifetimes

When a lifetime parameter 'a is declared on a function (fn run<'a, F>(f: F) where F: Fn(&'a str)), the caller chooses 'a before run begins executing. Therefore, 'a must outlive the entire call to run, and run cannot pass a reference to a local variable created inside run to f!

// ❌ does not compile
fn run_with_local<'a, F>(f: F)
where
    F: Fn(&'a str),
{
    let local_buffer = String::from("created inside run_with_local");
    // Error: `local_buffer` only lives for the body of `run_with_local`,
    // which is strictly shorter than any caller-chosen lifetime `'a`!
    f(&local_buffer);
}

fn main() {}
error[E0597]: `local_buffer` does not live long enough
 --> src/main.rs:9:7
  |
2 | fn run_with_local<'a, F>(f: F)
  |                   -- lifetime `'a` defined here
...
6 |     let local_buffer = String::from("created inside run_with_local");
  |         ------------ binding `local_buffer` declared here
...
9 |     f(&local_buffer);
  |     --^^^^^^^^^^^^^-
  |     | |
  |     | borrowed value does not live long enough
  |     argument requires that `local_buffer` is borrowed for `'a`
10| }
  | - `local_buffer` dropped here while still borrowed

Solving Local Borrowing with for<'a>

Replacing the function-level 'a parameter with the higher-ranked bound for<'a> F: Fn(&'a str) universally quantifies 'a across all lifetimes, allowing run_with_local to invoke f with borrows of local stack variables created inside the function:

Caller-Chosen Lifetime (`fn run<'a, F: Fn(&'a str)>`):
  Caller picks ONE `'a` (must outlive `run()`) ---> Cannot borrow locals inside `run()`!

Higher-Ranked Bound (`fn run<F: for<'a> Fn(&'a str)>`):
  `F` works for ALL `'a` -----------------------> Can borrow temporary locals inside `run()`!
fn run_with_locals<F>(f: F)
where
    F: for<'a> Fn(&'a str) -> usize,
{
    let first = String::from("short-lived local 1");
    let second = String::from("short-lived local 2!");

    println!("len 1 = {}, len 2 = {}", f(&first), f(&second));
}

fn main() {
    run_with_locals(|s| s.len());
}
len 1 = 19, len 2 = 20

Implicit HRTB Elision on Fn, FnMut, and FnOnce Bounds

Because higher-ranked lifetimes are almost always what you want when writing a closure bound that takes references, Rust's lifetime elision rules automatically expand elided lifetimes in Fn(...) trait bounds and fn(...) pointer types into for<'a> HRTBs:

Elided Syntax Desugared Higher-Ranked Form
F: Fn(&str) -> bool F: for<'a> Fn(&'a str) -> bool
F: Fn(&str, &str) -> &str Rejected (ambiguous return lifetime; requires explicit for<'a> Fn(&'a str, &'a str) -> &'a str)
F: Fn(&Context<'_>) F: for<'a, 'b> Fn(&'a Context<'b>)
fn(&u8) -> &u8 for<'a> fn(&'a u8) -> &'a u8