Lifetimes

While the Rust compiler does have the ability to infer the scopes of variables (and any references they may have) to an extent, there are situations where the compiler may need to be manually told how long certain values will exist in memory, so the program can be safely executed with no undefined behaviour.

When working with references as parameters and return types in functions and methods, it's not always obvious to the compiler whether it's memory safe to use them all. If the original values the references were pointing to were to go out of scope and get dropped from memory, the references would be dangling, that is, pointing to uninitialised memory.
Until it is confirmed that the compiling program isn't threatening to allow this, the compiler will refuse.

In the following example, the function takes a string slice and explicitly returns a string slice:

Here, the compiler infers that the string input and the string output last as long as each other.


    

The following example, however, is not so simple:

error[E0106]: missing lifetime specifier
--> src/main.rs:1:31
|
1 | fn f(fst: &str, snd: &str) -> &str {
|             ----       ----     ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `fst` or `snd`
help: consider introducing a named lifetime parameter
|
1 | fn f<'a>(fst: &'a str, snd: &'a str) -> &'a str {
|      ++++       ++            ++          ++

For more information about this error, try `rustc --explain E0106`.

A lifetime, separate from a scope (and explicitly denoted with an apostrophe and a short alias), is the memory-safe lifespan of a function, variable or reference. The lifetime of a reference is dependent on the lifetime of the variable it is referencing.

In the above example, the compiler can't work out which parameter relates to the returned reference, so it suggests adding explicit lifetime parameters to specify that:

A similar error also arises if a function is specified as returning a reference, but takes no parameters:

error[E0106]: missing lifetime specifier
--> src/main.rs:1:11
|
1 | fn f() -> &str {
|             ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
|
1 | fn f() -> &'static str {
|              +++++++
help: instead, you are more likely to want to return an owned value
|
1 | fn f() -> String {
|             ~~~~~~

For more information about this error, try `rustc --explain E0106`.

Absolutely no inference can be made here, because there aren't any references in the parameters to compare to the return value.

Note the message in the above erroneous example suggesting use of the static lifetime. This is a reserved alias for lifetimes that last for the duration of the entire program.

Explicit lifetime parameters share the same angle bracket syntax as generics. If a function uses generics and lifetime parameters at the same time, these need to be defined in the same brackets:

The lifetime specifier 'a used here means that the function, its parameter and its return value all share the same lifetime.


    

Lifetime specifiers need aliases for the same reason that generics need them - multiple generics of different aliases are inferred to mean that those generics are different types, whereas multiple lifetimes with different aliases are inferred to mean that those lifetimes are all of different lengths.

This example takes two generic parameters of explicitly different lifetimes, meaning that the parameter val can potentially last for a differing period of time than the parameter arr during execution:

Note the colon in the generic definition: lifetime 'b is being explicitly defined inline as lasting longer than 'a, and the Rust compiler will attempt to enforce these bounds whenever this function is called.


    

Exercise:
The following function concat() takes three string slices and returns a string struct. Refactor this function so that: