Back to Tutorials

Error Handling: Python Exceptions vs Rust Result

10 min readBeginner

Error handling in Rust is very different from Python. Instead of exceptions, Rust encourages you to handle errors explicitly with Result. Let's explore how this works and why it can lead to more reliable code.

Python Exceptions: Flexible but Dangerous

Python uses exceptions to signal errors. You write your normal logic and catch errors only when needed. This is powerful but can make it easy to forget to handle failures:

Division with Potential Error

Python
def divide(a, b):
    return a / b

try:
    result = divide(10, 0)
except ZeroDivisionError as e:
    print(f"Error: {e}")
Rust
fn divide(a: f64, b: f64) -> f64 {
    a / b  // ⚠️ Will panic at runtime if b == 0
}

// Rust prefers handling errors explicitly

Rust's Result Type: Errors as Values

Rust encourages you to handle errors as part of the type system. Instead of throwing exceptions, functions return a Result type:

Explicit Error Handling

Python
def safe_divide(a, b):
    if b == 0:
        return None
    return a / b

result = safe_divide(10, 2)
if result is None:
    print("Divide by zero!")
else:
    print(result)
Rust
fn safe_divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Divide by zero".to_string())
    } else {
        Ok(a / b)
    }
}

match safe_divide(10.0, 2.0) {
    Ok(result) => println!("{}", result),
    Err(e) => println!("Error: {}", e),
}

Propagating Errors with ?

In Rust, the ? operator simplifies error propagation by returning early if an error occurs:

Error Propagation

Python
def read_file(path):
    with open(path) as f:
        return f.read()

try:
    content = read_file("hello.txt")
except IOError as e:
    print(f"Failed to read file: {e}")
Rust
use std::fs::File;
use std::io::{self, Read};

fn read_file(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    Ok(content)
}

When Rust Panics

Rust does have panics, similar to Python exceptions, but they’re meant for unrecoverable errors. You should reserve panic! for bugs, not expected runtime errors.

Explicit Panic

Python
raise Exception("This should never happen")
Rust
panic!("This should never happen");

Key Takeaways

  • Rust's Result type: forces you to handle errors explicitly
  • No exceptions: Rust avoids hidden control flow
  • ? operator: ergonomically propagates errors
  • Panics: should be rare and only used for unrecoverable logic errors

Coming from Python

If you're used to Python's dynamic and flexible error handling, Rust's approach might feel verbose. But in return, you get strong compile-time guarantees and fewer surprises at runtime. With Result, you always know what might fail — and what you have to do about it.

Pro Tip: Use Result for anything that can fail, and use ? to keep your code clean while still being safe.