To panic! or Not to panic!

In this chapter, we'll explore guidelines for choosing between panic and result.

To panic! or Not to panic!

When should you use panic! versus returning Result? Here are guidelines:

When to panic

Examples and Prototypes

When prototyping, unwrap and expect are handy placeholders:

let home = "127.0.0.1".parse().unwrap()

Tests

Tests should panic on failure:

#[test]
fn test_something() {
    let result = some_function()
    assert_eq(result, expected_value)  // Panics if not equal
}

When You Have More Information

If you know the code can't fail, unwrap is fine:

let home = "127.0.0.1".parse().unwrap()  // Valid IP, won't fail

Invalid State

Panic when continuing would be dangerous or nonsensical:

fn divide(a: int, b: int) -> int {
    if b == 0 {
        panic("Division by zero")
    }
    return a / b
}

When to Return Result

Library Functions

Libraries should return Result to let callers decide how to handle errors:

fn read_file(path: string) -> Result<string, Error> {
    // Return Err instead of panicking
}

Expected Failure Cases

When failure is expected and recoverable:

fn find_user(id: int) -> Result<User, NotFoundError> {
    // User might not exist - that's not a bug
}

External Resources

When dealing with I/O, networks, etc.:

fn fetch_data(url: string) -> Result<Data, NetworkError> {
    // Network might fail - caller should handle
}

Summary

Use panic! for:

  • Bugs (programming errors)
  • Unrecoverable situations
  • Tests and examples

Use Result for:

  • Expected failures
  • Library APIs
  • Any situation the caller might want to handle

Next, let's explore Generic Types, Traits, and Lifetimes in Chapter 10.