Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can you specify multiple generic lifetimes on functions?

Tags:

rust

lifetime

When you create a function with multiple references as inputs, and return a reference as an output, you need to specify which one, or multiple input references the lifetime of the output reference is tied to. This makes sense.

The thing that doesn't make sense is why you would ever need to define more than one generic lifetime. You can only ever have one return value.

Here, we define both 'a and 'b - two generic lifetimes. On the return value, we can either specify 'a or 'b - not both:

fn foo<'a, 'b>(ref1: &'a str, ref2: &'b str) -> &'a str {}

It seems like this could be shortened to:

fn foo<'a>(ref1: &'a str, ref2: &str) -> &'a str {}

If we wanted to tie the lifetime of the output to the second input argument instead, we could do:

fn foo<'a>(ref1: &str, ref2: &'a str) -> &'a str {}

If we wanted to tie it to both, we can do:

fn foo<'a>(ref1: &'a str, ref2: &'a str) -> &'a str {}

This covers every scenario (at least in this simple example), and none of these require defining more than one generic lifetime (defining 'b).

Is there ever a case where you do need to define more than one generic lifetime?

like image 515
John Avatar asked Oct 20 '25 14:10

John


1 Answers

If you have only a single lifetime in your return value, you don't need more than one lifetime parameter on the function. All of these examples you asked about are valid Rust code:

fn foo<'a, 'b>(ref1: &'a str, ref2: &'b str) -> &'a str {} // borrows ref1 ('b is unnecessary)
fn foo<'a>(ref1: &'a str, ref2: &str) -> &'a str {}        // borrows ref1 (same as above)
fn foo<'a>(ref1: &str, ref2: &'a str) -> &'a str {}        // borrows ref2
fn foo<'a>(ref1: &'a str, ref2: &'a str) -> &'a str {}     // borrows both ref1 and ref2

Unnecessary lifetime parameters are allowed to have names, but because they are only used once, they don't impose any lifetime relationships on the function and may be elided (left out).

Is there ever a case where you do need to define more than one generic lifetime?

Sure, for example, when your function has more than one output lifetime:

fn longest_last<'a, 'b: 'a>(arg1: &'a str, arg2: &'a str, arg3: &'b str) -> (&'a str, &'b str) {
    (longest(arg1, longest(arg2, arg3)), arg3)
}

This rather silly function returns a tuple containing two references: one to the longest of the 3 strings, and one that always refers to arg3. The first reference has a lifetime which must be outlived by all three arguments; the second reference need only be outlived by arg3.

Related questions

  • When is it useful to define multiple lifetimes in a struct?
  • What is lifetime elision in very simple terms? (and other questions linked there)
  • Why are explicit lifetimes needed in Rust?
like image 77
trent Avatar answered Oct 23 '25 03:10

trent