&'a Foo<'a>
is completely fine to use provided that:
Foo<'a>
is covariant in'a
.- You do not intend to obtain a longer-lived reference from the
Foo
than you can with this definition.
To clarify the second point, consider these two possible structs:
struct One<'slice> {
foo: &'slice [&'slice str],
}
struct Two<'slice, 'strings> {
foo: &'slice [&'strings str],
}
If you have Two
, you can copy out &'strings str
references from it. If you have One
, you cannot do that — you can only copy out &'slice str
s that are only valid as long as the whole slice reference is.
So, in your original case, if Foo<'a>
has no public API by which you can obtain &'a SomethingElse
or SomethingElse<'a>
, or if Bar
never uses Foo
in that way, then there is no disadvantage to using &'a Foo<'a>
.
You only need two lifetime parameters if you intend to make use of the difference between them, or if there is invariance.
There are also cases where you cannot use two lifetime parameters; in particular, constructing a linked list that points up the stack (or uses an arena, or is in any other way made of references):
struct Node {
value: String,
children: Vec<Node>,
}
struct Context<'a> {
value: &'a str,
parent: Option<&'a Context<'a>>,
}
fn walk(node: &Node, ctx: Option<&Context<'_>>) {
let ctx = Context {
value: &node.value,
parent: ctx,
};
for child in &node.children {
walk(child, Some(&ctx));
}
}
If you try to make this code have Context<'a, 'b>
(or try to use &mut Context
references), you'll find you need Context<'a, 'b, 'c, ...>
and so on to infinity — but it works fine with the single lifetime.