Why use the word 'generic' in lifetime parameters?

688 views Asked by At

When we write:

fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
    x
}

Why don't we just refer to the 'as and the 'bs as lifetime parameters as opposed to generic lifetime parameters? It's just a syntactical way to communicate to the compiler a constraint on the returned reference's lifetime in terms of the lifetimes of the arguments. I'm struggling to see the rationale for the inclusion of the word "generic" here.

2

There are 2 answers

0
Alexey Romanov On BEST ANSWER

"Generic" makes me think of generic types, but as far as I can tell it has no relevance here.

You can think of "generic type" as a shorthand for "type with generic parameters", but those generic parameters can be any or all of:

  1. generic type parameters (as in Vec<T>);

  2. generic lifetime parameters (as in type Ref<'a> = &'a i32);

  3. generic const parameters (real soon now).

Same for "generic function" = "function with generic parameters".

You could shorten it to "lifetime parameters" exactly because all lifetime parameters (not all lifetimes!) are generic. The same applies to type parameters.

but I still don't see how that makes 'a generic as opposed to simply a parameter which takes various values

Lifetime parameters and type parameters have a few things in common which don't have to go together, but do in Rust:

  1. the most obvious: they are written between < > and not ( );

  2. they can be inferred;

  3. code can't branch depending on their value.

So they are grouped together as "generic parameters" as opposed to "value parameters".

4
pretzelhammer On

We use the word "generic" in front of "lifetime parameters" because they are generic lifetime parameters.

The obvious counter-example is 'static which is the only non-anonymous lifetime so we can refer to it outside of generic contexts. On the other hand, since all other possible lifetimes are anonymous the only way we can refer to them is through generic lifetime parameters.

The foo function below can be called with theoretically infinitely many different possible lifetimes, and it works in all those situations because it's generic over lifetimes. Here's an example of calling it with 2 different lifetimes:

fn foo<'a>(x: &'a i32) -> &'a i32 { x }

fn main() {
    foo(&1); // called with 'static lifetime
    let x = 1;
    foo(&x); // called with x's anonymous lifetime
    // and so on
}

The lifetime-constraint relationship between the input argument and the output return type in both calls is the same but the lifetimes are different.