Recipe
Trace a panic to its cause
Use when: a program stopped with a message that says panicked at, and you need to find the bug
Goal: the line that panicked, the value that was wrong there, and the line that made that value
Taught in Panics
Quick recipe
-
1
The first line says
panicked at, then a file, a line and a column. The number after the thread's name is a thread id. It changes every run, so ignore it.Example
thread 'main' (63710710) panicked at src/main.rs:6:5:points at the filesrc/main.rs, line 6, column 5.Why: the place is where one of Rust's checks failed, so that's where to start.
-
2
The next line says what went wrong. Match it to its kind.
Example
attempt to divide by zeroon line 6 ofaveragemeans thatscores.len()was 0.index out of bounds: the len is N but the index is N— an index equal to the length, often a count used as a position.attempt to divide by zero— a divisor that was 0.- your own message from
panic!,todo!orunreachable!— the code stopped on purpose, so read what it says.
Why: the reason tells you which value to go looking for.
-
3
Open the file at that line and find the value the reason names.
Example
Line 6 is
total / scores.len() as u32.scoresis a parameter, so the empty list came from a caller.- the bad value was made on this line — fix it here.
- the bad value came in as a parameter — go on to Step 4 to find the caller.
Why: the line that notices a problem is often not the line that caused it.
-
4
Set the environment variable
RUST_BACKTRACEto1and run the program again. The panic now prints a backtrace: the stack of calls at that moment.Example
On macOS or Linux, run
RUST_BACKTRACE=1 cargo run. In PowerShell, run$env:RUST_BACKTRACE = 1, thencargo run.Why: the plain message names one line; the backtrace names every call that led to it.
-
5
Skip the frames that belong to Rust itself: names that start with
core::,std::or__rustc::. The first frame with your package's name is where the panic happened. Each frame below it is the caller of the one above. Read down until you reach the line that made the bad value.Example
Frames 0 to 2 are Rust's. Frame 3,
scores::averageat line 6, is the panic. Frame 4,scores::reportat line 10, passed the list on. Frame 5,scores::mainat line 15, calledreport(&[]). The empty list on line 15 is the bug.Why: each caller handed a value down, so the cause sits at or below the frame that panicked.
Common mistakes
- Fixing the line that panicked when the bad value came from a caller.
- Reading the backtrace from the bottom up, or stopping at Rust's own frames at the top.
- Taking the number after
'main'for a line number. It's a thread id. - Waiting for a backtrace without setting
RUST_BACKTRACE. The plain message only suggests it.