What if you could stop writing bulky error checks and still catch every problem perfectly?
Why Propagating errors with ? in Rust? - Purpose & Use Cases
Imagine you are writing a program that reads a file, processes its content, and then saves the result. Without a simple way to handle errors, you have to check after every step if something went wrong and write extra code to handle each case.
This manual error checking makes your code long, hard to read, and easy to mess up. You might forget to check an error or write repetitive code that clutters your main logic.
The ? operator lets you quickly pass errors up the chain without writing extra code. It stops the current function and returns the error if something goes wrong, keeping your code clean and easy to follow.
let file = File::open("data.txt"); if file.is_err() { return Err(file.unwrap_err()); } let file = file.unwrap();
let file = File::open("data.txt")?;
You can write clear, concise functions that automatically handle errors and focus on the main task.
When building a web server, you often read configuration files and database connections. Using ? helps you quickly stop and report errors without messy code everywhere.
Manual error checks clutter code and cause mistakes.
? operator simplifies error handling by propagating errors automatically.
Cleaner code means easier maintenance and fewer bugs.
Practice
? operator do in Rust when used with a Result type?Solution
Step 1: Understand the
The?operator behavior?operator checks if theResultis anErr. If yes, it returns the error from the current function immediately.Step 2: What happens if
If theResultisOk?ResultisOk, it unwraps the value inside and continues execution.Final Answer:
It returns the error immediately if there is one, otherwise unwraps the value. -> Option CQuick Check:
?returns error or unwraps value [OK]
? returns error early or unwraps success [OK]- Thinking
?converts Result to Option - Assuming
?ignores errors - Believing
?retries operations
? operator inside the function to propagate std::io::Error?Solution
Step 1: Identify return types compatible with
The??operator works only in functions returningResultorOption.Step 2: Check each signature
fn read_file() -> String returnsStringdirectly, no error type, so?can't be used. fn read_file() -> Result returnsResult, which supports?. fn read_file() -> () returns unit(), no error handling. fn read_file() -> Option<String> returnsOption, which supports?for absence but cannot propagatestd::io::Error.Final Answer:
fn read_file() -> Result -> Option BQuick Check:
Function must return Result or Option for?[OK]
Result or Option return type to use ? [OK]- Using
?in functions returning plain types - Forgetting to specify error type in Result
- Confusing
OptionandResultreturn types
fn parse_number(s: &str) -> Result {
let num = s.parse::<i32>()?;
Ok(num * 2)
}
fn main() {
match parse_number("10") {
Ok(n) => println!("{}", n),
Err(e) => println!("Error: {}", e),
}
}Solution
Step 1: Understand
The function tries to parse the string "10" to an integer usingparse_numberfunctions.parse::<i32>()?. Since "10" is valid, it unwraps to 10.Step 2: Calculate the returned value
It multiplies the parsed number by 2, so 10 * 2 = 20, then returnsOk(20).Step 3: Check
The match prints the number 20 because parsing succeeded.mainoutputFinal Answer:
20 -> Option AQuick Check:
Valid parse returns doubled number [OK]
? returns doubled value [OK]- Assuming parse fails and prints error
- Confusing output with input string
- Thinking code won't compile due to
?
? and fix it:fn get_first_char(s: &str) -> Result {
let first = s.chars().next()?;
Ok(first)
}Solution
Step 1: Analyze
next()return typenext()returns anOption<char>, but?requiresResultorOptioncompatible with function return.Step 2: Fix error propagation
Since function returnsResult, convertOptiontoResultusingnext().ok_or("Empty string".to_string())?to propagate error properly.Final Answer:
Changenext()tonext().ok_or("Empty string".to_string())?-> Option AQuick Check:
Convert Option to Result for?in Result function [OK]
ok_or before ? [OK]- Using
?directly on Option in Result function - Ignoring error conversion from Option to Result
- Replacing
?with.unwrap()causing panic
?. Which of the following implementations correctly uses ? to propagate errors?Solution
Step 1: Check function return type compatibility
The function returnsResult, so it can use?to propagate IO errors.Step 2: Analyze each option
fn count_lines(path: &str) -> Result { let content = std::fs::read_to_string(path)?; Ok(content.lines().count()) } uses?correctly and returnsOkwith line count. fn count_lines(path: &str) -> usize { let content = std::fs::read_to_string(path)?; content.lines().count() } returnsusizedirectly but uses?which is invalid here. fn count_lines(path: &str) -> Result { let content = std::fs::read_to_string(path).unwrap(); Ok(content.lines().count()) } usesunwrap()which panics on error, not propagates. fn count_lines(path: &str) -> Option<usize> { let content = std::fs::read_to_string(path)?; Some(content.lines().count()) } returnsOptionbutread_to_stringreturnsResult, so?cannot be used directly.Final Answer:
fn count_lines(path: &str) -> Result<usize, std::io::Error> { let content = std::fs::read_to_string(path)?; Ok(content.lines().count()) } -> Option DQuick Check:
Return Result and use?to propagate errors [OK]
? usage for error propagation [OK]- Using
?in functions without Result or Option return - Using
unwrap()instead of propagating errors - Mismatching return types and error handling
Ready to prove it?
Pass the test and this pattern moves unknown
