My expectation is that 'a will be mapped to the lifetime of local_a, and that I will be able to successfully run and compile the code. However, when I attempt to do so, the following error message is output:
error[E0597]: `local_a` does not live long enough
--> src/main.rs:3:12
|
1 | fn foo<'a>(foo_fn: &dyn Fn(&'a u32)) {
| -- lifetime `'a` defined here
2 | let local_a: u32 = 1;
| ------- binding `local_a` declared here
3 | foo_fn(&local_a)
| -------^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `local_a` is borrowed for `'a`
4 | }
| - `local_a` dropped here while still borrowed
Does an annotated lifetime for a function need to last, at minimum, for the duration of the function its declared on? If not, what is the cause of the error? Also, how can I modify the code to accomplish what I want (i.e pass an Fn with annotated lifetimes in to a function where it can operate on local references in that function)?
I always find it helpful to think of lifetime parameters just like any other generic parameter. Which is to say, the caller (your main function calling foo) gets to choose what 'a is, not the callee (foo itself). You are violating this contract by giving foo_fn a reference that isn't living for 'a, but instead lives for the anonymous local lifetime of the scope of foo. To be able to pass a local variable via reference to foo_fn, you need to use a higher-ranked lifetime instead of a generic lifetime parameter on foo:
The shorter answer is yes, a generic parameter on a function is at least just longer than the function body. Another way to phrase it is that the caller chooses the lifetime, and the caller cannot name/choose a lifetime that's shorter than the function call.
In either case, you can never borrow a local for as long as a generic lifetime on the function, because locals are moved or dropped by the time the function call is over, and those are both incompatible with being borrowed.
// Accepts references with only one specific lifetime, which the caller
// chooses (and is longer than the function body)
// vvvvvvvvvvvvvvv
fn foo<'a>(foo_fn: &dyn Fn(&'a u32)) {
// Must accept *any* lifetime `'a`, including those too short for the
// caller to name (like borrows local to this function)
// vvvvvvvvvvvvvvvvvvvvvvv
fn foo<'a>(foo_fn: &dyn for<'a> Fn(&'a u32)) {
Higher-ranked types and bounds (those for<'a> things) are ways to let the caller supply something that works with lifetimes shorter than they can name.
// Must accept *any* lifetime `'a`, including those too short for the
// caller to name (like borrows local to this function)
// vvvvvvvvvvvvvvvvvvvvvvv
fn foo(foo_fn: &dyn for<'a> Fn(&'a u32)) {
// ^^ ^^^^ ^^
// || this introduces 'a this uses 'a
// no lifetime declared here
The for<'a> (HRTB) basically requires that foo_fn itself is generic over lifetimes.
&dyn Fn(&'a u32) would correspond to this function structure (if foo_fn was declared inside foo):
fn foo<'a>() {
// a specific lifetime
// vv
fn foo_fn(&'a u32)
}
While for<'a> &dyn Fn(&'a u32), it would be like this: