We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 12 of 24
`?` on Option, and bridging to Result
Two functions. One uses ? on Option end to end; the other bridges from
Option into Result.
pub fn nth_word(s: String, n: usize) -> Option<String>
pub fn nth_word_len(s: String, n: usize) -> Result<usize, String>
nth_word returns the nth whitespace-separated word of the first line of
s, owned, counting from zero. None if there is no first line, or no word
at that index.
nth_word_len returns the byte length of that word, or
Err("no word at index {n}").
So ("the cat sat", 1) gives Some("cat") and Ok(3); ("the cat sat", 9)
gives None and Err("no word at index 9"). Only the first line is
considered: in "alpha beta\ngamma delta", word index 2 does not exist.
? on Option
? is not a Result feature — it works on anything that implements the (still
unstable) Try trait, and Option is one of them:
let line = s.lines().next()?; // -> &str, or return None
let word = line.split_whitespace().nth(n)?; // -> &str, or return None
Some(word.to_string())
Two early exits, three lines, no nesting. And note what ? does on Option:
it returns None from the enclosing function. No conversion happens — there
is no error payload to convert. On Result the Err goes through
From::from; on Option there is nothing to do.
The starter’s E0277, verbatim
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s,
in a function that returns `Result`
|
| let word = nth_word(s, n)?;
| ^ use `.ok_or(...)?` instead
This is the single most common early ? failure, and the message is so good
that learning to read it is most of the lesson. Rust will not silently pick
an error value for you when an Option runs dry — you have to say what the
absence means, and ok_or_else is where you say it:
let word = nth_word(s, n).ok_or_else(|| format!("no word at index {n}"))?;
The mirror-image error, for a ? on a Result inside an Option-returning
function, reads “the ? operator can only be used on Options, not
Results”. Its fix is .ok()?.
The version of this error that actually gets people stuck
Once you have a real error type rather than a String, the same mistake
often stops looking like a ? problem at all:
error[E0271]: type mismatch resolving `<i32 as FromStr>::Err == MyErr`
|
| let n: i32 = s.parse()?;
| ^^^^^ expected `MyErr`, found `ParseIntError`
Same underlying cause — ? cannot get from the error you have to the error
your function returns — but phrased in terms of an associated type
(FromStr::Err) instead of in terms of ?. If you meet E0271 with
== in it, mentally rewrite it as: “you asked ? to convert
ParseIntError into MyErr, and there is no From impl that does that.”
Writing that From impl is item 6.15, and it is where all of this becomes
painless.
Why .unwrap() is not the bridge
The gate denies clippy::unwrap_used and clippy::expect_used for this
problem, so nth_word(s, n).unwrap() will not compile. That is deliberate.
unwrap on the None path does not bridge anything — it aborts the process.
Bridging means choosing an error value, and ok_or/ok_or_else is the only
API that makes you do it.
(These two lints are allow by default in clippy, and denied only in this
track. Turning them on course-wide is what makes clippy feel arbitrary — the
point is to force the habit while it is being learned.)
Notes
-
Eager vs lazy applies here too:
ok_or(format!(…))builds the message on every call including the successful ones.ok_or_else(|| format!(…))builds it only on theNonepath. -
?inside a closure needs the closure’s return type to be inferable. If you get a bewildering inference error from a closure containing?, annotate it:|x| -> Result<i32, String> { … }. -
split_whitespacenever yields an empty word and collapses runs of spaces, so" spaced out "has word 0 ="spaced". -
str::lenis bytes."wörld"is 6 bytes and 5 characters.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.