We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership III: Lifetimes, Explicitly step 8 of 22
`longest`: writing your first `'a`
This is the one where you write a lifetime annotation that actually does work.
pub fn longest(a: &str, b: &str) -> &str // needs an annotation
pub fn longest_of(items: &[String]) -> Option<&str> // does not
longest returns whichever argument is longer by len(), i.e. by bytes;
on a tie it returns a. longest_of returns the longest element of the slice,
first-wins on ties, and None for an empty slice.
Why longest cannot elide
Two parameters carry lifetimes, so elision rule 2 does not fire and you get E0106: missing lifetime specifier. The compiler is asking a question: which input does the output come from? Here the honest answer is “either one, I decide at runtime”, and the way to say that is:
pub fn longest<'a>(a: &'a str, b: &'a str) -> &'a str
Re-read what that means, because the intuitive reading is wrong. It does not
say “a and b live equally long”. It says: there exists some region 'a
over which both inputs are valid, and the result is valid over that same
region. At each call site the compiler picks the largest region satisfying
that — effectively the overlap of the two inputs’ validity. Lifetimes are
constraints, not durations; nothing about this annotation changes when anything
is dropped.
The generated test driver leans on exactly that. It builds a in the outer
scope, builds b inside a nested block, and calls longest(&a, &b) in there.
'a is inferred as the inner region, the result is usable inside the block,
and would be rejected outside it. That is the constraint doing its job, not a
limitation.
The two errors you will meet on the way
The starter ships the annotation almost everybody writes first:
pub fn longest<'a, 'b>(a: &'a str, b: &'b str) -> &'a str
Two independent regions, and the return promises 'a. Returning b then
fails with a bare, un---explain-able diagnostic:
error: lifetime may not live long enough
... function was supposed to return data with lifetime `'a`
but it is returning data with lifetime `'b`
= help: consider adding the following bound: `'b: 'a`
No error code. Get used to it — the harder lifetime failures often have
none, and hunting for rustc --explain on this one is a dead end. (The
suggested 'b: 'a bound would compile, and it is a legitimate signature. It
is also strictly more machinery than <'a> on both, so it is not what you
want here.)
Now half-fix it and leave b elided:
pub fn longest<'a>(a: &'a str, b: &str) -> &'a str
Returning b from that gives E0621: explicit lifetime required in the type
of b: lifetime 'a required. Same underlying complaint, different shape,
and this one does have a code. Two spellings of the same mistake, two
completely different diagnostics — which is precisely why “read the error” is
a skill rather than a slogan.
Why longest_of must not be annotated
longest_of(items: &[String]) has exactly one lifetime-carrying parameter, so
elision hands its lifetime to the &str inside the Option for free. Writing
longest_of<'a>(items: &'a [String]) -> Option<&'a str> compiles and is
correct — and still fails this problem, because clippy::needless_lifetimes
is warn-by-default and the gate is -D warnings. One function in this file
needs 'a and the other must not have it. That contrast is the lesson.
The tie trap
items.iter().max_by_key(|s| s.len()) returns the last maximum, not the
first. Its documentation says so, and it is a real bug generator. The tests
include ties. Reach for reduce with a strict > comparison, or fold, or a
loop — anything whose tie-breaking you chose on purpose.
The pins
const _: for<'a> fn(&'a str, &'a str) -> &'a str = longest;
const _: for<'a> fn(&'a [String]) -> Option<&'a str> = longest_of;
These coerce your functions into function-pointer types with the lifetimes
spelled out. Return a String from either, or over-tie the lifetimes, and the
coercion stops compiling. Do not edit or delete them.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.