3

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
John
  • 2,551
  • 3
  • 30
  • 55
  • 2
    "You can only ever have one return value." What about `-> (&str, &str)`? Or arguments of type `&mut &T` (which are used to return values by assignment inside the function)? – trent Apr 05 '21 at 17:26
  • "It seems like the above example could be shortened to: **" – trent Apr 05 '21 at 17:27
  • @trentcl I'm not certain, but I can't imagine it would be possible to have a tuple where one reference is expired, while the other is still valid. Wouldn't the entire tuple be invalid if either reference in it is invalid? – John Apr 05 '21 at 17:30
  • @Shepmaster you can have a single type (a tuple) with multiple lifetimes? One for each element? – John Apr 05 '21 at 17:32
  • 1
    I wrote an answer, but I'm not completely sure I've answered the question, so if this (and the linked questions) don't resolve your question, please clarify as best you can. Thanks! – trent Apr 05 '21 at 17:59

1 Answers1

6

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

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
trent
  • 25,033
  • 7
  • 51
  • 90
  • 2
    Since you've linked to [When is it useful to define multiple lifetimes in a struct?](https://stackoverflow.com/q/29861388/155423), I'll point out that if you accept those answers, then you almost need to have a function to _construct_ those types in the first place. – Shepmaster Apr 05 '21 at 18:21