In Rust, there are no traditional exceptions like in Java or C++; instead, the wrapper types Result and Option are used for returning errors.
Result<T, E> — holds either a value (Ok(T)) or an error (Err(E)). It is typically used for operations that may fail (e.g., file I/O).
Option<T> — for optional values: either Some(T) or None, without error information. Example:
fn divide(x: f64, y: f64) -> Option<f64> { if y == 0.0 { None } else { Some(x / y) } }
Rust does not have exceptions in the try/catch style to ensure a transparent, managed error flow without requiring a garbage collector or unwind engine to maintain exceptions.
Why should panic! not be used for normal error handling?
Answer: panic! terminates the execution flow, does not allow recovery, and leads to unwinding/abort. If errors are returned through Result, the calling side can handle them. Example:
fn foo() -> Result<(), String> { Err("Smth went wrong".to_string()) } // instead of fn foo() { panic!("Smth went wrong"); }
Story
In a microservice for image processing, a developer used
panic!to handle all exceptional situations. This led to the service crashing on any error instead of returning a proper HTTP response to the client with error details.
Story
In a CLI tool,
unwrap()was used for all input-output, not handling possible errors. As a result, on a filesystem error, the program simply terminated without any messages or diagnostics.
Story
In an analytics service, they attempted to use Option for every minor error, losing information about the errors themselves, making debugging more difficult. After switching to Result with enum errors, it became easier to find and fix bugs.