the lifetime elision rules
If every function that touched a reference forced you to write out lifetime parameters by hand, Rust code would be drowning in 'a and 'b. In practice you almost never write them. The reason is the lifetime elision rules: a small, fixed set of patterns the compiler applies to fill in the obvious lifetimes for you. They are not a guess and not magic — they are three deterministic rules that succeed only when the answer is unambiguous, and when they cannot decide, the compiler simply asks you to write the lifetimes yourself.
There are exactly three rules, applied in order to a function signature. Rule one: each input reference that has no written lifetime gets its own fresh, distinct lifetime parameter (one &T input gets 'a, a second gets 'b, and so on). Rule two: if there is exactly one input lifetime, that lifetime is assigned to every output reference. So fn first(s: &str) -> &str is read by the compiler as fn first<'a>(s: &'a str) -> &'a str, which is what you meant. Rule three: if one of the inputs is &self or &mut self (a method), the lifetime of self is assigned to all output references — because methods overwhelmingly return borrows tied to the receiver. If after applying all three rules an output reference still has no lifetime, elision has failed and the compiler errors, asking you to annotate. That is why the longest function from the previous entry needs an explicit 'a: it has two input references, so rule two does not apply, and there is no self, so rule three does not apply.
Why this matters: elision is a readability convenience, not a change in meaning. The annotated and elided versions compile to exactly the same constraints; the elided form is just sugar the compiler expands. A common misconception is that omitting lifetimes makes the borrow checking 'looser' or skips it — it does not. The checker runs identically; elision only decides whether you or the compiler wrote the labels. When elision guesses a relationship you did not intend (rare, but possible with multiple inputs), the fix is to write the lifetimes explicitly to say exactly which output borrows from which input.
// One input reference -> rule 2 ties the output to it. No 'a needed. fn first_word(s: &str) -> &str { s.split(' ').next().unwrap_or("") } // Compiler reads it as: // fn first_word<'a>(s: &'a str) -> &'a str // Two input references and no self -> elision FAILS, you must annotate. // fn pick(a: &str, b: &str) -> &str { a } // error: missing lifetime
Elision is three deterministic rules; when they cannot pin down every output lifetime, the compiler asks you to.
Elision never relaxes the borrow check; it only decides who writes the labels. If the three rules cannot resolve every output lifetime, the program does not compile until you annotate it yourself.