is there ever benefit to requiring references in a struct have the same lifetime
Yes, and it goes beyond having a struct. If lifetimes were always distinct from each other, then you couldn't write this function:
fn foo<'a, 'b>(a: &'a str, b: &'b str) -> &str {
// What lifetime to return?
if (global_random_number() == 42) {
a
} else {
b
}
}
Applying to the struct, you could have something like this:
struct EvenOrOdd<'a, 'b> {
even: &'a str,
odd: &'b str,
}
impl<'a, 'b> EvenOrOdd<'a, 'b> {
fn do_it(&self, i: u8) -> &str {
if i % 2 == 0 {
self.even
} else {
self.odd
}
}
}
Note that while this compiles, it doesn't return a string that can outlive the structure itself, which is not what was intended. This code fails, even though it should be able to work:
fn foo<'a, 'b>(a: &'a str, b: &'b str) {
let result = { EvenOrOdd { even: a, odd: b }.do_it(42) };
println!("{}", result);
}
This will work with unified lifetimes:
struct EvenOrOdd<'a> {
even: &'a str,
odd: &'a str,
}
impl<'a> EvenOrOdd<'a> {
fn do_it(&self, i: u8) -> &'a str {
if i % 2 == 0 {
self.even
} else {
self.odd
}
}
}
This is the opposite of the linked answer, which has the comment:
you want to be able to take an aggregate value and split off parts of it after using it
In this case, we want to take an aggregate value and unify them.
In rarer occasions, you may need to thread the needle between distinct and unified lifetimes :
struct EvenOrOdd<'a, 'b: 'a> {
even: &'a str,
odd: &'b str,
}
impl<'a, 'b> EvenOrOdd<'a, 'b> {
fn do_it(&self, i: u8) -> &'a str {
if i % 2 == 0 {
self.even
} else {
self.odd
}
}
}
While this is useful when needed, I can't imagine the wailing and gnashing of teeth that would erupt if we had to write it this way every time.
ignoring the extra typing
I wouldn't. Having
foo<'a>(Bar<'a>)
is definitely better than
foo<'a, 'b', 'c, 'd>(Bar<'a, 'b', 'c, 'd>)
When you aren't benefiting from the extra generic parameters.