We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership III: Lifetimes, Explicitly step 6 of 22
Let elision do it
Four small string functions. The starter gives all four an explicit <'a>.
Three of them do not need it. One of them does. Find the one.
pub fn longest_line(s: &str) -> &str
pub fn head_tail(s: &str) -> (&str, &str)
pub fn nth_field(s: &str, n: usize) -> &str
pub fn trim_prefix(s: &str, prefix: &str) -> &str // <- one of these signatures is a lie
What each one does
-
longest_line— the longest line ofsby byte length. Lines are split on\n. On a tie the first one wins. Empty input gives"". -
head_tail— split at the first ASCII space: everything before it, and everything after it. No space at all means(s, ""). -
nth_field— split on,and return field numbern, zero-indexed. Out of range gives"". -
trim_prefix— ifsstarts withprefix, return the rest ofs; otherwise returnsunchanged.
The actual exercise
Elision assigns output lifetimes for you when it can. Rule 2 fires only when exactly one parameter carries a lifetime at all — then that lifetime goes to every elided output. Look at the four signatures with that rule in hand:
-
longest_line(&str) -> &str— one lifetime-carrying parameter. Elides. -
head_tail(&str) -> (&str, &str)— still one. Both outputs get it. Elides. -
nth_field(&str, usize) -> &str—usizecarries no lifetime, so there is still exactly one that does. Elides. -
trim_prefix(&str, &str) -> &str— two lifetime-carrying parameters. Rule 2 does not apply, the output lifetime is unknown, and you get E0106: missing lifetime specifier. This is the one you must annotate — and the annotation is a decision, not a formality.
Two lints are the graders here
Both are warn-by-default, and this problem compiles with -D warnings, so
a warning is a hard failure:
-
clippy::needless_lifetimes— fires on an explicit lifetime that elision would have produced anyway. All three stray<'a>s in the starter trip it. -
clippy::extra_unused_lifetimes— fires on a declared lifetime that is never used.
This is deliberate. “You usually don’t need annotations” is easy to nod along
with and hard to act on; here it is mechanically enforced. Note that
needless_lifetimes is not triggered by the annotation on trim_prefix —
it stays quiet on lifetimes that genuinely link things elision could not link.
The call-site pin
At the bottom of the file:
const _: () = {
fn _pin(s: &str) -> &str {
let prefix = String::from("//");
trim_prefix(s, &prefix)
}
};
This is the point of the whole problem, so read it carefully. It borrows a
local String as the prefix, and returns a reference derived from s,
which came from the caller. That is a completely reasonable thing for a caller
to want: the prefix was a scratch value, the result is a view into the real
input.
Write trim_prefix<'a>(s: &'a str, prefix: &'a str) -> &'a str — the “just tie
everything together” reflex — and it stops compiling with E0515: 'a is
now forced to cover prefix too, so it can be no longer than a local that is
about to be dropped. The signature compiled, the body worked, and the
function was still wrong, because it over-promised on the caller’s behalf.
Only trim_prefix<'a>(s: &'a str, prefix: &str) -> &'a str — “the answer comes
from s, and prefix is nobody’s business but mine” — passes.
Do not edit or delete the pin. A submission without it is a failed submission.
Byte semantics, on purpose
longest_line compares len(), which is bytes, not characters. One of the
hidden cases feeds you multibyte text so the difference is not academic. All
the split points here (\n, ' ', ,) are ASCII, so every slice you produce
lands on a character boundary.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.