Error Propagation Patterns
In this page:
fn name() -> Result<T, Box<dyn Error>> {
let value = step()?;
Ok(value)
}
impl From<Source> for NameError {
fn from(e: Source) -> Self { NameError::Variant(e) }
}
Propagating with ? Across Functions
A helper function can propagate an error with ?, letting its caller (or the caller's caller) ultimately decide how to react, rather than making that decision too early.
Example: Propagating with ? Across Functions
// Define the function `parse_positive` taking `s` and returning a `Result<i32, String>`
fn parse_positive(s: &str) -> Result<i32, String> {
// Declare an immutable variable `n` (an integer), set to `s.parse().map_err(|_| String::from("not a number"))?`
let n: i32 = s.parse().map_err(|_| String::from("not a number"))?;
// Check whether `n < 0`
if n < 0 {
// Return `Err(String::from("must be positive"))` from this function
return Err(String::from("must be positive"));
}
Ok(n)
}
// Entry point: execution of the program starts here
fn main() {
// Print "{:?}" to the console, substituting in `parse_positive("5")` (with a trailing newline)
println!("{:?}", parse_positive("5"));
// Print "{:?}" to the console, substituting in `parse_positive("-3")` (with a trailing newline)
println!("{:?}", parse_positive("-3"));
}
Login to try C/C++/Java/PHP code in the editor
Using Box<dyn Error> for Multiple Error Types
When a function can fail in multiple unrelated ways (e.g. parsing errors and custom validation errors), returning Box<dyn std::error::Error> lets it unify them under one flexible return type.
Example: Using Box<dyn Error> for Multiple Error Types
// Bring `std::error::Error` into scope
use std::error::Error;
// Define the function `parse_and_check` taking `s` and returning a `Result<i32, Box<dyn Error>>`
fn parse_and_check(s: &str) -> Result<i32, Box<dyn Error>> {
// Declare an immutable variable `n` (an integer), set to `s.parse()?`
let n: i32 = s.parse()?;
Ok(n * 2)
}
// Define the function `main` with no parameters and returning a `Result<(), Box<dyn Error>>`
fn main() -> Result<(), Box<dyn Error>> {
// Declare an immutable variable `value`, set to `parse_and_check("21")?`
let value = parse_and_check("21")?;
// Print "Value: {}" to the console, substituting in `value` (with a trailing newline)
println!("Value: {}", value);
Ok(())
}
Login to try C/C++/Java/PHP code in the editor
Adding Context While Propagating
A middle-layer function can catch an error, wrap it with more context using map_err, and then propagate the enriched error further up, giving callers a clearer picture of what happened.
Example: Adding Context While Propagating
// Define the function `load_setting` taking `raw` and returning a `Result<i32, String>`
fn load_setting(raw: &str) -> Result<i32, String> {
raw.parse::<i32>().map_err(|e| format!("failed to load setting: {}", e))
}
// Entry point: execution of the program starts here
fn main() {
// Match on `load_setting("abc")` and branch by pattern
match load_setting("abc") {
Ok(v) => println!("Setting: {}", v),
Err(e) => println!("{}", e),
}
}
Login to try C/C++/Java/PHP code in the editor
main Returning a Result
Rust's main function itself can return a Result, allowing the top-level program to use ? directly and print a clean error message automatically if something fails.
Example: main Returning a Result
// Define the function `main` with no parameters and returning a `Result<(), std::num::ParseIntError>`
fn main() -> Result<(), std::num::ParseIntError> {
// Declare an immutable variable `value` (an integer), set to `"99".parse()?`
let value: i32 = "99".parse()?;
// Print "Parsed: {}" to the console, substituting in `value` (with a trailing newline)
println!("Parsed: {}", value);
Ok(())
}
Login to try C/C++/Java/PHP code in the editor
- Handling every possible error deep inside a low-level helper function instead of letting it bubble up to a caller who has more context.
- Forgetting
Box<dyn Error>is a common flexible return type when a function can produce several different unrelated error types. - Overusing
.unwrap()deep in a call chain instead of propagating the error upward with?, which crashes the whole program instead of letting a caller decide.
- Propagating errors upward with
?lets higher-level code decide how to handle or report failures. Box<dyn std::error::Error>is a common return type for functions that may fail in more than one distinct way.- Layered functions can each add their own context before propagating an error further up the call stack.
- Good error propagation keeps low-level functions simple while concentrating decision-making at higher levels.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: