R
RUSTY YELLOW PAGES

for<'a> TypeLifetime


Explanation

for<'a> Type is a higher-ranked trait bound (HRTB): a bound that says "this must hold for every lifetime 'a, not one specific lifetime chosen ahead of time." It shows up almost exclusively on trait bounds involving a reference-taking closure or function, most commonly Fn/ FnMut/FnOnce — a bound like F: for<'a> Fn(&'a str) -> bool requires F to work for any lifetime the caller ends up picking, not one fixed lifetime chosen where the bound is written, as the complete function below demonstrates.

To see why this is needed, compare it to the bound without for<'a>: F: Fn(&'a str) -> bool would require 'a to be some one concrete lifetime, fixed at the point the bound is written — but at that point, no such lifetime exists yet, because the string reference f will actually be called with doesn't exist until apply runs and constructs one from data with its own, unrelated lifetime. A single fixed 'a can't describe "whatever lifetime a locally-created &str happens to have inside this function body" — that lifetime is different on every call, and often shorter than anything nameable in the surrounding function signature. for<'a> sidesteps the whole problem: instead of picking one lifetime up front, it requires F to work no matter which lifetime shows up, which is exactly the guarantee needed to call f with a reference that's only born inside apply's own body.

In practice, the compiler infers for<'a> automatically for the common Fn(&str) -> bool-shaped bound — writing F: Fn(&str) -> bool (lifetime elided) implicitly means F: for<'a> Fn(&'a str) -> bool. The explicit for<'a> syntax becomes necessary to write out by hand once the bound needs to name that lifetime elsewhere in the same signature, or once elision genuinely can't produce the higher-ranked bound on its own (for instance, a bound spanning multiple arguments that must share one universally-quantified lifetime).

Usage examples

Accepting a closure that works for any lifetime

fn find_first<'s>(text: &'s str, matches: impl for<'a> Fn(&'a str) -> bool) -> Option<&'s str> {
    // <- `for<'a>`: the closure must accept a &str of ANY lifetime, not one fixed lifetime
    text.split_whitespace().find(|word| matches(word))
}

find_first("sensor offline retry", |w| w.starts_with("r"));

Writing generic code

A function that accepts a validation closure and calls it with several short-lived string slices, each borrowed only for the duration of one iteration, needs the closure's bound to hold for all of those independently-scoped borrows at once — exactly what for<'a> expresses.

fn validate_all<F>(entries: &[String], is_valid: F) -> bool
where
    F: for<'a> Fn(&'a str) -> bool, // <- must accept a &str with whatever lifetime each iteration produces
{
    entries.iter().all(|entry| is_valid(entry))
}

let readings = vec!["21.5".to_string(), "22.0".to_string(), "19.8".to_string()];
let all_positive = validate_all(&readings, |value| {
    value.parse::<f64>().map(|v| v > 0.0).unwrap_or(false)
});

Each call to is_valid inside the loop passes a reference whose lifetime is tied to that specific iteration — no single named lifetime in validate_all's own signature could stand in for all of them, so the bound has to be universally quantified over every possible lifetime via for<'a>, as the Rust Reference's higher-ranked trait bounds section describes.

Designing a public API

A callback-taking API that stores the closure and invokes it later, with borrowed data whose lifetime differs on every invocation, must bound the closure with for<'a> rather than a single named lifetime parameter on the containing function.

struct Parser<F> {
    on_token: F,
}

impl<F> Parser<F>
where
    F: for<'a> Fn(&'a str), // <- one bound covering every future call, each with its own lifetime
{
    fn run(&self, input: &str) {
        for token in input.split_whitespace() {
            (self.on_token)(token); // <- `token`'s lifetime is different (and shorter) on every iteration
        }
    }
}

let parser = Parser { on_token: |t: &str| println!("token: {t}") };
parser.run("start measuring stop");

Giving Parser its own named lifetime parameter and bounding F: Fn(&'p str) against it would tie every call to one fixed lifetime 'p chosen when Parser is constructed — but the &strs passed to on_token are created fresh inside run, with a shorter lifetime than 'p could ever be; for<'a> is the only bound shape that accepts a closure usable across all of those independently-scoped calls.

Explanation

Embedded support: Full

Mechanically, a higher-ranked trait bound means exactly the same thing under #![no_std] — it's resolved entirely at compile time, with zero runtime representation either way, so there's no such thing as a no_std-specific rule for for<'a>. What differs is how often it comes up: HRTBs are genuinely rarer in day-to-day embedded code than in, say, generic hosted libraries that hand callers a closure taking &str. Most embedded-hal traits are written around concrete, already-named types (&mut self, a fixed Word associated type) rather than generic closures parameterized over a borrow the trait itself doesn't own, so the shape that makes for<'a> necessary — a callback invoked later with a reference whose lifetime the callback's own signature can't name in advance — doesn't arise as often.

The one place it does show up regularly is critical-section helpers. critical_section::with hands the closure a CriticalSection<'cs> token whose lifetime 'cs is chosen internally, fresh, by with itself — not by the caller, and not fixed at the point the bound is written. That's the same shape as the classic page's Fn(&'a str) -> bool example: the compiler infers the higher-ranked bound automatically from FnOnce(CriticalSection<'_>) -> R, but writing it out by hand as for<'cs> FnOnce(CriticalSection<'cs>) -> R becomes necessary the moment a generic wrapper function needs to name that bound explicitly in its own signature, as in the second example below.

Usage examples

Reading a shared value from inside a critical section

use core::cell::Cell;
use critical_section::Mutex;

static TICKS: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));

fn ticks() -> u32 {
    critical_section::with(|cs| {
        // <- the closure's parameter type is `CriticalSection<'cs>` for whichever `'cs` `with` supplies;
        //    the implicit bound on `with`'s closure argument is `for<'cs> FnOnce(CriticalSection<'cs>) -> R`
        TICKS.borrow(cs).get()
    })
}

Naming the bound explicitly in a generic wrapper

use core::cell::Cell;
use critical_section::Mutex;

static TICKS: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));

fn read_shared<F, R>(f: F) -> R
where
    F: for<'cs> FnOnce(critical_section::CriticalSection<'cs>) -> R,
    // <- `for<'cs>`: `f` must accept whatever lifetime `critical_section::with` supplies when it calls it
{
    critical_section::with(f)
}

fn ticks() -> u32 {
    read_shared(|cs| TICKS.borrow(cs).get())
}