John
John

Reputation: 2952

Why can you specify multiple generic lifetimes on functions?

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?

Upvotes: 4

Views: 522

Answers (1)

trent
trent

Reputation: 28025

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

Upvotes: 7

Related Questions