The lifetime for the returned slice is inferred from its usage. To prevent accidental misuse, it’s suggested to tie the lifetime to whichever source lifetime is safe in the context, such as by providing a helper function taking the lifetime of a host value for the slice, or by explicit annotation.
How exactly is this lifetime inferred when we do not tie it using a helper function?
Lifetimes, unlike type parameters, are allowed to not be fully inferred. What I mean by this: While the type-checker needs to figure out what every type in your program is (except as far as that type’s lifetime arguments are concerned) unambiguously, and without conflicting requirements, so there must never be multiple types that could still be possible, nor may requirements contradict (leaving no possible type to infer), the situation the borrow-checker figuring out lifetimes is different.
Lifetimes in Rust don’t influence program behavior so there’s no need to unambiguously fully figure them out (infer them) in order to know how your program should behave. As long as the borrow-checker determines there are no conflicts in the requirements your code imposes on the lifetimes in question, it will happily accept the code; further compilation doesn’t care about lifetimes anymore anyways, and the borrow-checker’s goal is nothing but to ensure soundness. There is thus no value in fully inferring lifetimes.[1]
I’ve brought this up e.g. in this post in a discussion about whether a language feature would be reasonable that could allow you to take any two types U and T and make your program behavior depend on whether or not the types T and U are the same. My argument against it there is that if T and U are the same type but with possibly different lifetimes, e.g. &'a mut i32 and &'b mut i32, the question boils down to asking the compiler whether or not 'a and 'b are the same lifetime.
With the context from above, that’s not something the compiler can actually answer, as the borrow-checker doesn’t answer questions like this: All it can do is say “does this program pass the check if I require these two lifetimes to be the same”. But if you don’t require them to be the same the true answer may just be “it’s not really determined/inferred” how these two lifetimes are related. And you may have situations where the borrow checker allows you to constrain 'a and 'b to be the same or to constrain 'b and 'c to be the same, but not both, which is what I give some code examples for in the linked post (same link again), so feel free to have a read
An interesting comparison is other programming languages which may use a more dynamic, or at least a more uniform, representation of data, and implement generics without any monomorphization. In such programming languages, often ambiguous types are fine, too – quite similar to the situation for Rust with lifetimes – since in that case their type checker is, again like Rust’t borrow checker, just a soundness check, and irrelevant for implementing program behavior. ↩︎
let mut n1 = 1;
let mut n2 = 2;
let mut r1 = &mut n1;
let mut r2 = &mut n2;
assert_same_type(&mut r1, &mut r2);
assert_same_type(&mut r2, &mut r3);
They're asserting that the types of r1 and r2, including their lifetimes, are the same. T must resolve to their type (&'lt mut i32 for some lifetime 'lt) and not some supertype (shorter lifetime) because they're in an invariant position (behind the outer &mut). The lifetime of the outer muts don't matter for this purpose, they're just supplying the invariance so the it doesn't assert the sameness for some shorter-lived reborrows.
The use of a single type parameter T is what's forcing them to be the same.