We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 11 of 24
Propagating with `?`
Run an input through four fallible steps, stopping at the first failure.
pub fn pipeline(input: String) -> Result<i32, String>
The four helpers are already written for you and must not be changed:
fn non_empty(s: &str) -> Result<&str, String> // "empty input"
fn strip_tag(s: &str) -> Result<&str, String> // "missing `n=` prefix in `42`"
fn to_number(s: &str) -> Result<i32, String> // "not a number: forty"
fn in_range(n: i32) -> Result<i32, String> // "out of range: 5000"
pipeline trims the input, then feeds it through all four in order. If any
step fails, its error comes straight back out unchanged. "n=42" gives
Ok(42); "n=5000" gives Err("out of range: 5000").
Your body should be four lines.
The starter is correct and unacceptable
It passes every test. It is also twenty lines of the same three-line incantation, and clippy will tell you so four times:
error: this `match` expression can be replaced with `?`
|
| let trimmed = match non_empty(input.trim()) {
| ^ help: try instead: `non_empty(input.trim())?`
clippy::question_mark is on by default and it recognises exactly this shape:
match r { Ok(v) => v, Err(e) => return Err(e) }. Which is worth pausing on,
because it tells you what ? is. ? is not exception handling. It is not a
throw. It is that match, spelled with one character:
expr?unwraps theOkand gives you the value, or returnsErrfrom the enclosing function — after passing the error throughFrom::from.
The From::from part does nothing here (every step already returns
Result<_, String>, and From<String> for String is the identity) but it is
the entire mechanism behind everything later in this track. Keep it in mind.
And the gate closes in the other direction too
Having discovered ?, the natural overcorrection is to use it on the last
step as well:
Ok(in_range(n)?) // don't
error: enclosing `Ok` and `?` operator are unneeded
help: remove the enclosing `Ok` and `?` operator
clippy::needless_question_mark is also on by default. Unwrapping a value out
of a Result and immediately putting it back in the same kind of Result is
a round trip that does nothing. The last step already has the right type — just
return it.
So both directions are gated: a match pyramid fails, and a ?-happy solution
fails. What survives is the version where each ? is doing real work.
Reading a ? chain
let trimmed = non_empty(input.trim())?;
let body = strip_tag(trimmed)?;
let n = to_number(body)?;
in_range(n)
Four lines, four exit points, and the happy path reads top to bottom with no
error handling in the way. That is the trade ? makes: it hides the failure
edges so the success path is legible. When you need the edges back — to add
context, or to recover — you go back to match, or to map_err.
One nuance that costs people an afternoon: ? returns from the enclosing
function or closure, not from the enclosing block. Putting a ? inside a
closure returns from that closure, which is a different thing from returning
from the function around it. That is exactly why try { … } blocks are a
separate (and still unstable) feature.
Notes
-
try_erris a restriction lint that fires onErr(e)?— a way of writingreturn Err(e.into())that people discover and then overuse. Prefer the plainreturn Err(...). -
Result<_, String>here is scaffolding, and you should not copy it. AStringerror cannot be matched on, cannot carry structured data, and allocates on every failure. Items 6.14 and 6.15 replace it with a real error type; this problem usesStringonly so that?needs no conversions yet. -
?is rejected in aconst fn(E0658) — theTrytrait itself is still unstable, so const support has nowhere to hang.
Remember the grade is compile + tests + clippy -D warnings.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.