Recipe
Hand an error up with ?
Use when: a function calls something that returns a Result, and the function itself isn't the right place to decide what to do about a failure
Goal: a function that returns Result and passes every failure to its caller
Taught in The question mark operator
Quick recipe
-
1
Change the return type from
TtoResult<T, E>, whereEis the error type of the calls inside.Example
fn count_lines(path: &str) -> usizebecomesfn count_lines(path: &str) -> Result<usize, io::Error>, becauseread_to_stringfails with anio::Error.Why:
?returns early with anErr, and a function can only return anErrif its return type is aResult. -
2
Replace each
matchthat returnsErr(e)early, or eachunwrap, with a?right after the call.Example
fs::read_to_string(path).unwrap()becomesfs::read_to_string(path)?.Why: each
?either gives the success value to the rest of the line or returns the error to the caller. -
3
Make the last expression
Ok(...)around the answer.Why: the function now returns a
Result, so a successful answer must be anOk. -
4
Every caller now receives a
Result. Each one either handles it (amatch, a fallback) or, if it isn't the right place either, repeats these steps.Why: the error has to end up somewhere that can respond to it, usually
mainor close to it.
Common mistakes
- Adding
?without changing the return type (E0277). - Forgetting the
Ok(...)around the final value (E0308, mismatched types). - Putting
?on a call whose error type differs from the function's, with no conversion between them (E0277, couldn't convert the error).