Rust treats most errors as recoverable using the Result type. But for serious, unrecoverable failures like bugs in logic or unsafe conditions, you use the panic! macro. It immediately stops the program and prints a message.
Rust is designed to help you write safe, reliable code. Most of the time, it encourages handling errors through Result. But sometimes, you want the program to crash immediately when something unexpected happens. That is what panic! is for.
In this post, I will explain when to use panic, how it works, and why it should be used only in rare situations.
If you have not read Error Handling with Result, you should read that first. It covers how to handle common failures safely.
What Is panic!?
panic! is a macro that causes the program to terminate immediately with an error message.
Example
fn main() {
panic!("Something went terribly wrong!");
}
This prints the message and a stack trace, then exits.
When to Panic
You should panic when:
- You reach a logic bug that should never happen
- Continuing would lead to corrupted data
- There is no reasonable way to recover
Examples include:
- Indexing beyond the bounds of an array
- Unwrapping a None from an Option that should never be None
- Failing assertions in tests
Rust encourages failing early and loudly if things go wrong unexpectedly.
Panicking on Indexing
Rust panics if you index out of range:
let nums = vec![1, 2, 3];
let value = nums[10]; // will panic
Use get() instead to avoid this:
match nums.get(10) {
Some(v) => println!("{}", v),
None => println!("Index out of bounds"),
}
panic! in Functions
You can also panic inside your own functions:
fn get_first_char(s: &str) -> char {
s.chars().next().expect("String was empty")
}
If the string is empty, expect() calls panic with a message.
panic vs Result
Use Result when the error is expected and recoverable, like:
- File not found
- User input is invalid
- Network failure
Use panic! for bugs in your code, like:
- A required invariant is broken
- A value should never be None or zero, but is
panic in Tests
Tests use panic to fail assertions:
#[test]
fn math_test() {
assert_eq!(2 + 2, 4);
}
If the test fails, assert_eq! triggers panic.
You can also write custom panic cases:
#[test]
#[should_panic]
fn this_should_fail() {
panic!("Expected failure");
}
Customizing panic behavior
By default, panic prints a message and backtrace. You can control it by setting the RUST_BACKTRACE environment variable:
RUST_BACKTRACE=1 cargo run
This gives you a full trace of the error.
Summary Table
Use panic when… | Use Result when… |
---|---|
A bug breaks your program’s logic | A failure is expected and can be handled |
You want to crash fast in unsafe conditions | You want to return and recover gracefully |
A test should fail if something goes wrong | You want to continue or retry an operation |
Final Word
Use panic! only when your program is in a state where continuing is unsafe or meaningless. Rust’s safety model helps you avoid panics in most real applications, but it is still a useful tool for catching serious bugs early.
Rust Learning Path
- Introduction to Rust
- Setting Up Rust Development Environment
- Your First Rust Program
- Variables and Mutability in Rust
- Constants and Immutability in Rust
- Type Annotations and Inference in Rust
- Variable Shadowing and Scope in Rust
- Rust Primitive Data Types
- Working with Strings in Rust
- Every Operator in Rust Explained
- Tuples in Rust
- Arrays and Slices in Rust
- Rust Decision Making: if & match
- Loops in Rust
- Reading Input from the Console in Rust
- Mastering Rust Functions (For Beginners)
- Understanding Ownership in Rust
- Borrowing and References in Rust
- Copy Types vs. Move Semantics in Rust
- Cloning Data in Rust
- Methods and Associated Functions in Rust
- Enums: Defining Variant Types in Rust
- Pattern Matching with Enums in Rust
- The Option Type: Null Safety in Rust
- Error Handling with Result
- Unrecoverable Errors in Rust
- Organizing Code with Modules in Rust
- Cargo Basics: Rust Package Manager
- How to Use External Crates in Rust