What does the lifetime parameter in the signature denote in the POV of the body when the calling side infers it is `'static`?

Consider this example:

struct A<'a>(&'a mut &'a ());
static mut EMPTYREF: &'static () = &();
static AVAR: A<'static> = A(unsafe { &mut *&raw mut EMPTYREF });

fn call<'a>(v: &'a A<'a>) {
    let r: &'a i32 = &0;
    //let rr:&'static i32 = r;  // #1
}
fn main() {
    call(&AVAR);
}

In this example, the lifetime parameter 'a, in the POV of the calling side, must be inferred to be 'static due to the invariance of the lifetime parameter of struct A, however, if #1 is uncommented, the compiler will report an error:

error: lifetime may not live long enough
 --> src/main.rs:7:12
  |
5 | fn call<'a>(v: &'a A<'a>) {
  |         -- lifetime `'a` defined here
6 |     let r: &'a i32 = &0;
7 |     let rr:&'static i32 = r;
  |            ^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`

So, what does the parameter lifetime declared in the signature denote in the POV of the function/implementation's body, even though it is inferred to be ''static' in the POV of the calling side?

Does it just denote a specific lifetime that lives in the whole body, regardless of how the calling side infers it? Even though it is inferred as 'static in the POV of the calling side, without having more constraints in the signature, it cannot have that property.

Yes, 'a is just any lifetime that lasts for at least the duration of the function. It only compiles if the function works for any such 'a. It's not relevant what happens in the caller.

When type checking functions, rust doesn't even look inside other functions.

1 Like

That is, even though the calling side infers the lifetime to be 'static, however, in the function or implementation's body, the lifetime parameter cannot carry the information corresponding to the calling, i.e., in the body, the lifetime parameter cannot be used as the lifetime inferred in the calling side right?

Yes, in Rust functions are type checked independently. When Rust checks main, it sees this:

fn call<'a>(v: &'a A<'a>) {
    // I don't know what's in here
}
fn main() {
    call(&AVAR);
}

and when it checks call, it sees this:

fn call<'a>(v: &'a A<'a>) {
    let r: &'a i32 = &0;
    //let rr:&'static i32 = r;  // #1
}
fn main() {
    // I don't know what's in here
}

This has many advantages. For example, if I make changes to call without changing the signature, that will never break main.