Practically speaking, the #1 reason you'll have to write lifetime annotations is because the compiler asks you so. It will reject function signatures which are not covered by lifetime elision rules.
I assume you would like an simple example where lifetimes are mandatory. Imagine the following scenario:
struct Blah<'a> {
hoy: &'a u8
}
fn want_a_hoy(blah: &Blah) -> &u8 {
blah.hoy
}
The intention is obvious, but the compiler doesn't handle it:
<anon>:7:35: 7:38 error: missing lifetime specifier [E0106]
<anon>:7 fn want_a_hoy(blah: &Blah) -> &u8 {
^~~
<anon>:7:35: 7:38 help: see the detailed explanation for E0106
<anon>:7:35: 7:38 help: this function's return type contains a borrowed value, but
the signature does not say which one of `blah`'s 2 elided
lifetimes it is borrowed from
In this case, annotations solve the problem:
fn want_a_hoy<'a, 'b>(blah: &'b Blah<'a>) -> &'a u8 {
blah.hoy
}
Here you're specifying 'a
twice (on Blah<'a>
and &'a
). This is the same lifetime! So what you're saying to the compiler here is: "This function takes a reference to a blah containing an inner reference. I will return something which lives exactly as long as the inner reference of the blah." In this case, the signature gives a strong hint that you're likely to return something coming from the innards of the blah.