Error Handling: The "Safety Net Contract"
In JavaScript, errors are “invisible.” You don’t know if a function will return null, undefined, or throw a hidden exception until your code crashes in production.
In Rust, Error Handling is an explicit Safety Net Contract. There is no null and no throw. Instead, you “Wrap” your data in specialized containers that force you to handle failure cases before you can access the success data.
🏗️ The 3-Step Narrative
1. Option<T>: The “Null” Killer
JavaScript: const x = findUser(id); might return null. Rust: It returns an Option.
Think of an Option as a Box that is either Full (Some) or Empty (None).
enum Option<T> { Some(T), None,}
let some_number = Some(5);let absent_number: Option<i32> = None; // ⬅️ You must be explicit if it's NoneIMPORTANT: The Rule
To get the value out of the
Somebox, you must handle theNonecase. You can’t just “forget” it exists.
2. Result<T, E>: The try/catch Replacement
Instead of throwing exceptions, Rust functions return a Result.
Ok(T)- Success with data.Err(E)- Error with info.
use std::fs::File;
fn main() { let result = File::open("hello.txt");
let file = match result { Ok(file) => file, // ⬅️ Success case Err(e) => panic!("File missing: {:?}", e), // ⬅️ Handle failure case };}3. The ? Operator: The “Bubble” Contract
If you don’t want to handle the error in the current function, you can use the ? operator to “bubble” it up to the caller.
fn read_username() -> Result<String, io::Error> { let mut s = String::new(); File::open("hello.txt")?.read_to_string(&mut s)?; // 🪄 Bubbling up errors Ok(s)}🔬 From the Official Book
🚫 panic!: The “Emergency Stop”
When Rust encounters a truly unrecoverable error, it Panics. This is like your program pulling its own emergency brake. Use this sparingly, only for things that should “never” happen.
fn main() { panic!("crash and burn"); // 💥 Everything stops here}💡 What Error Handling Unlocks
- Compile-Time Safety: You can no longer have “null pointer exceptions” because there is no
null. - Predictable Flow: Every error becomes a known value that you can handle using pattern matching.
- Maintainable Code: When you see
?in a function, you know exactly where the failure points are without searching fortry/catchblocks. - Better API Design: By returning
ResultandOption, your function names become honest about whether they might fail.