Panic!
In Rust, a panic is an unrecoverable error that causes the program to stop execution immediately. Panics are used for situations where the program cannot continue and there's no way to recover. Understanding when and how panics occur is crucial for writing robust Rust programs.
What is a Panic?
A panic occurs when your program encounters an error that it cannot handle. When a panic happens, Rust will:
- Print an error message
- Unwind the stack (clean up resources)
- Exit the program
Result<T, E> instead. Panics
should be rare in production code.
Using the panic! Macro
You can explicitly cause a panic using the panic! macro:
panic!("crash and burn");
When this code runs, the program will stop and print:
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` for a backtrace
Click Run to execute your code
When Panics Occur
Panics can occur in several situations:
1. Explicit panic! Calls
if value < 0 {
panic!("Value cannot be negative");
}
2. Index Out of Bounds
let v = vec![1, 2, 3];
let element = v[10]; // Panics! Index 10 doesn't exist
v.get(10) instead, which
returns Option<&T> and won't panic.
3. Division by Zero
let result = 10 / 0; // Panics!
4. Calling unwrap() on None or Err
let none: Option<i32> = None;
let value = none.unwrap(); // Panics!
unwrap() and expect()
Two common methods that can cause panics are unwrap() and
expect():
unwrap()
let some_value = Some(5);
let value = some_value.unwrap(); // Returns 5
let none_value: Option<i32> = None;
let value = none_value.unwrap(); // Panics!
unwrap() should only
be used in examples, tests, or when you're absolutely certain the value exists.
In production code, handle errors properly with match or
Result.
expect()
expect() is like unwrap(), but allows you to provide a
custom error message:
let result: Result<i32, &str> = Ok(42);
let value = result.expect("Failed to get value"); // Returns 42
let error: Result<i32, &str> = Err("something went wrong");
let value = error.expect("Failed to get value"); // Panics with message
expect() is slightly better
than unwrap() because it provides context about what went wrong,
making debugging easier.
Safe Alternatives to unwrap()
Instead of using unwrap(), use these safer alternatives:
1. Using match
let maybe_value: Option<i32> = Some(10);
match maybe_value {
Some(v) => println!("Value: {}", v),
None => println!("No value"),
}
2. Using if let
if let Some(v) = maybe_value {
println!("Value: {}", v);
}
3. Using unwrap_or()
let value = maybe_value.unwrap_or(0); // Returns 10 or 0 if None
4. Using unwrap_or_else()
let value = maybe_value.unwrap_or_else(|| {
// Compute default value
0
});
Backtraces
When a panic occurs, Rust can print a backtrace, which shows the call stack leading to the panic. This is extremely useful for debugging.
To see a backtrace, run your program with:
RUST_BACKTRACE=1 cargo run
Or set it permanently:
export RUST_BACKTRACE=1 # On Unix/Linux/Mac
set RUST_BACKTRACE=1 # On Windows
Click Run to execute your code
When to Use Panic vs Result
Use panic! when:
- Unrecoverable errors: When there's no way to recover from the error
- Programming errors: When the error indicates a bug in your code (e.g., index out of bounds)
- Examples and tests: When writing example code or tests where panics are acceptable
- Prototyping: During early development when you want to fail fast
Use Result<T, E> when:
- Recoverable errors: When the caller can handle the error
- I/O operations: File operations, network requests, etc.
- User input: Parsing, validation, etc.
- Production code: When you need robust error handling
Best Practices
- Avoid unwrap() in production: Always handle errors properly
- Use expect() for better messages: If you must panic, use expect() with a descriptive message
- Prefer Result for recoverable errors: Use Result when the caller can handle the error
- Use safe alternatives: Use get(), unwrap_or(), or match instead of unwrap()
- Enable backtraces in development: Set RUST_BACKTRACE=1 for debugging
- Document panic conditions: If your function can panic, document when it happens
Common Mistakes
1. Using unwrap() everywhere
// Wrong - Can panic at runtime
let value = some_option.unwrap();
let result = some_result.unwrap();
// Correct - Handle errors properly
match some_option {
Some(v) => println!("{}", v),
None => println!("No value"),
}
match some_result {
Ok(v) => println!("{}", v),
Err(e) => println!("Error: {}", e),
}
2. Using index syntax without checking bounds
// Wrong - Panics if index doesn't exist
let v = vec![1, 2, 3];
let element = v[10]; // Panic!
// Correct - Use get() for safe access
match v.get(10) {
Some(element) => println!("{}", element),
None => println!("Index out of bounds"),
}
3. Panicking on recoverable errors
// Wrong - Should use Result
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Division by zero"); // Bad!
}
a / b
}
// Correct - Return Result
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Division by zero"))
} else {
Ok(a / b)
}
}
4. Not enabling backtraces during development
// Always set RUST_BACKTRACE=1 when debugging
// export RUST_BACKTRACE=1 (Unix/Linux/Mac)
// set RUST_BACKTRACE=1 (Windows)
// This helps you see exactly where panics occur
Exercise: Panic Handling Practice
Task: Implement safe functions that handle errors without panicking.
Requirements:
- Write a safe division function using Option
- Write a safe vector access function
- Write a username validation function using Result
- Avoid using unwrap() or expect()
Click Run to execute your code
Show Solution
fn safe_divide(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None
} else {
Some(a / b)
}
}
fn safe_get(v: &Vec<i32>, index: usize) -> Option<&i32> {
v.get(index)
}
fn validate_username(username: &str) -> Result<String, String> {
if username.len() < 3 {
Err(String::from("Username must be at least 3 characters"))
} else if username.contains(' ') {
Err(String::from("Username cannot contain spaces"))
} else {
Ok(String::from(username))
}
}
Summary
- Panic is for unrecoverable errors
- Use
panic!macro to explicitly panic unwrap()andexpect()can cause panics- Use
match,if let, orunwrap_or()for safe handling - Enable backtraces with
RUST_BACKTRACE=1for debugging - Use
panic!for programming errors,Resultfor recoverable errors - Avoid
unwrap()in production code - Panics unwind the stack and exit the program
- Use safe alternatives like
get()instead of index syntax
What's Next?
Now that you understand panics, you'll learn about Result<T, E> - Rust's way of handling recoverable errors. The Result type allows you to return either a success value or an error, and the caller can decide how to handle it. This is the preferred way to handle errors in Rust.
Enjoying these tutorials?