Rust Error : “lifetime may not live long enough” — why &static fn(T) - T doesn’t mean what you think
You’ve got a tidy little generic: pub struct Test<T> {
f: &'static fn(T) -> T,
} …and Rust fires back: the parameter type T may not live long enough help: consider adding a bound T: 'static Why is the lifetime of a reference to a function pointer tangled up with the lifetime of T? After all, code lives forever, right? Let’s unpack what the compiler is protecting you from, and then fix it in a few idiomatic ways.
The core intuition
- &'static fn(T) -> T says: I hold a reference that’s valid for the entire program to a function which takes a T and returns a T.
- If T is not 'static, it might hide a short-lived borrow inside it, e.g. T = &'a i32.
- A 'static reference to that function can be called after 'a has ended. If the function returns that &'a i32, you get a reference that points to a dead stack frame. 💥
Rust doesn’t let you promise the function pointer outlives everything while letting the T it manipulates be allowed to carry “short” lifetimes. Hence the nudge: “add T: 'static”. In short: a 'static handle to a function that produces a T forces T to be 'static, otherwise you could manufacture dangling references.
Two clean fixes (pick one) 1) Don’t borrow the function pointer at all Plain function pointers are already 'static. There’s rarely a reason to take &fn(...) instead of fn(...): pub struct Test<T> {
f: fn(T) -> T, // no reference, no lifetime trouble
} This compiles without T: 'static, because the function pointer value is 'static but doesn’t force T to be 'static. You can still call it with T = &'a i32 and get a &'a i32 back, and the borrow checker will ensure you only use that within 'a. When to choose this: You only need real, free-standing functions (not closures), and you don’t need to borrow a pointer to the pointer.
2) Make the function reference non-'static If you truly want to borrow a function pointer (or a callable) from somewhere, tie it to a caller-provided lifetime: pub struct Test<'a, T> {
f: &'a fn(T) -> T, // or: // f: &'a dyn Fn(T) -> T, // if you want closures too
} Now the function reference is only guaranteed to be valid for 'a, so using it can’t outlive the lifetimes that may be hidden inside T. When to choose this: You’re passing in a callable that you don’t own (e.g., a borrowed closure or a borrowed function pointer) and you want the struct to be valid only as long as that callable is.
“But why isn’t &'static fn(T) -> T okay for some T?” Imagine (hypothetically) you could do this: fn make_static_fn<T>(_: T) -> &'static fn(T) -> T { todo!() } let f: &'static fn(_) -> _; {
let a = 42; // Pretend the compiler allowed it: f = make_static_fn(&a); // here T is `&'a i32`
} // Much later … let r = (f)(&0); // From the type system’s POV, `r` has lifetime `'a` (!) If this compiled, r would be a reference that must still borrow a from that inner block—even though a is long gone. That’s the kind of time travel Rust refuses to enable. The 'static on the reference doesn’t just say “the code lives forever”—it anchors the ability to produce T forever, and that only makes sense if T itself can live forever (T: 'static).
Bonus: closures vs function pointers
- fn(T) -> T is a function pointer type: only free functions (or non-capturing closures, which can coerce) fit here.
- dyn Fn(T) -> T is a closure trait object: it can capture environment. If you want to hold closures, use Box<dyn Fn(T) -> T> or &'a dyn Fn(T) -> T.
Example with a borrowed, capturing closure: pub struct Test<'a, T> {
f: &'a dyn Fn(T) -> T,
} fn demo() {
let x = 10;
let add_x = |n: i32| n + x; // captures `x`
let t = Test { f: &add_x }; // `t` can’t outlive `x`
assert_eq!((t.f)(5), 15);
} Advanced pattern: higher-ranked lifetimes (HRTB) Sometimes you want a function that works for any lifetime it’s given, e.g. a function that takes a reference and returns that same reference. You can write that with an explicit for<'a>: type IdRef = for<'a> fn(&'a i32) -> &'a i32; pub struct Test {
f: IdRef, // callable for any `'a`
} This avoids baking a specific 'a into the type. You generally won’t combine this with a generic T because the lifetime is inside T; instead, name the reference type directly as above.
Practical checklist
- ✅ Prefer fn(T) -> T over &fn(T) -> T. The pointer itself is 'static; avoid borrowing it.
- ✅ If you must borrow, add a lifetime: Test<'a, T> { f: &'a fn(T) -> T } (or &'a dyn Fn).
- ✅ Only require T: 'static when you truly need to store the callable (or its outputs) beyond any borrow.
- ✅ Use dyn Fn if you need closures; use fn for plain functions.
- ✅ Reach for HRTB (for<'a>) when you need “same-lifetime-in, same-lifetime-out” across any 'a.
Putting it all together Here are two idiomatic, compiling versions you can drop in today: Function pointer (no borrow, most ergonomic): pub struct Test<T> {
f: fn(T) -> T,
} impl<T> Test<T> {
pub fn new(f: fn(T) -> T) -> Self { Self { f } }
pub fn call(&self, t: T) -> T { (self.f)(t) }
} Borrowed callable with closures support: pub struct Test<'a, T> {
f: &'a dyn Fn(T) -> T,
} impl<'a, T> Test<'a, T> {
pub fn new(f: &'a dyn Fn(T) -> T) -> Self { Self { f } }
pub fn call(&self, t: T) -> T { (self.f)(t) }
} Both avoid the unsound promise that a 'static-lived callable can create non-'static values forever.
Summary You got the error because &'static fn(T) -> T would let you produce T forever—even when T secretly contains short-lived borrows. Rust blocks that unless you assert T: 'static. The easy, idiomatic fixes are:
- Don’t borrow the function pointer: use fn(T) -> T.
- Or borrow it with a non-'static lifetime: &'a fn(T) -> T / &'a dyn Fn(T) -> T.
Once you make that change, the compiler — and your future self — will breathe easier.