RustArena

Step 2 of 7

Reading panics and backtraces

When a Rust program hits a bug it can't recover from, it panics: it stops, prints a message to stderr, and exits with code 101. The message is precise, so read it slowly.

fn main() {
    let lines = ["12", "7", "x3"];
    let mut total = 0;
    for line in lines {
        let n: i32 = line.parse().expect("each line should be a whole number");
        total += n;
        println!("running total: {total}");
    }
}
running total: 12
running total: 19

Then, on stderr:

thread 'main' (31337) panicked at src/main.rs:5:35:
each line should be a whole number: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The fix is to handle bad lines instead of crashing:

fn main() {
    let lines = ["12", "7", "x3"];
    let mut total = 0;
    for line in lines {
        match line.parse::<i32>() {
            Ok(n) => total += n,
            Err(e) => eprintln!("skipping {line:?}: {e}"),
        }
    }
    println!("total: {total}");
}
total: 19

Messages you'll meet

Message Usual cause
index out of bounds: the len is 3 but the index is 3 a loop or index one past the end
called `Option::unwrap()` on a `None` value a lookup or search found nothing
called `Result::unwrap()` on an `Err` value: ... parsing or I/O failed; the error follows
attempt to subtract with overflow an unsigned number would go below 0 (debug builds check this)
attempt to divide by zero dividing by an empty collection's length, often
RefCell already borrowed a RefCell borrowed mutably while another borrow was alive
not yet implemented a todo!() was reached

Backtraces

The panic location says where it broke, but often the real mistake is in the caller. Set the RUST_BACKTRACE environment variable to see the whole chain of calls, the backtrace:

fn average(scores: &[u32]) -> u32 {
    let total: u32 = scores.iter().sum();
    total / scores.len() as u32
}

fn main() {
    let rounds: Vec<Vec<u32>> = vec![vec![70, 80], vec![]];
    for r in &rounds {
        println!("{}", average(r));
    }
}
$ RUST_BACKTRACE=1 cargo run
75

thread 'main' (8226) panicked at src/main.rs:3:5:
attempt to divide by zero
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/2d8144b7.../library/std/src/panicking.rs:689:5
   1: core::panicking::panic_fmt
             at /rustc/2d8144b7.../library/core/src/panicking.rs:80:14
   2: core::panicking::panic_const::panic_const_div_by_zero
             at /rustc/2d8144b7.../library/core/src/panicking.rs:175:17
   3: scores::average
             at ./src/main.rs:3:5
   4: scores::main
             at ./src/main.rs:9:24
   5: <fn() as core::ops::function::FnOnce<()>>::call_once
             at /rustc/2d8144b7.../library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Read it from the top and skip the frames from std and core (the panic machinery). Frame 3 is the first one in your crate: scores::average at line 3. Frame 4 is who called it: main, line 9. So main passed an empty round, and average didn't expect one. On Windows PowerShell, set the variable with $env:RUST_BACKTRACE=1 first. RUST_BACKTRACE=full shows every frame.

Common mistakes

Your task

This program panics before finishing. Run it, read the panic message, and fix the loop so it prints the change between each pair of neighboring readings:

18 -> 21: +3
21 -> 19: -2
19 -> 25: +6

Challenge 2

This order summary panics with called `Option::unwrap()` on a `None` value. Find out which item causes it, and handle the missing price instead of panicking: print kiwi: unknown item and leave it out of the total. Expected output:

apple: 120
kiwi: unknown item
pear: 95
total: 215

Challenge 3

The warehouse program panics with attempt to subtract with overflow. Stock is a u32, which can't go below 0. Use checked_sub (it returns None instead of overflowing) so an order larger than the stock is refused and the program keeps going:

shipped 2, 3 left
cannot ship 4, only 3 left
shipped 1, 2 left

Previous: dbg! and eprintln!: print debugging done right Next: Debuggers and logging