RustArena

Step 1 of 7

dbg! and eprintln!: print debugging done right

When code does something unexpected, the quickest tool is still printing values. Rust has two macros made for it.

dbg!

dbg!(expression) prints the file, line and column, the expression's source text, and its value (pretty-printed with {:#?}), then returns the value. So you can wrap any expression in place without restructuring your code:

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

fn main() {
    let avg = average(&[70, 85, 90]);
    eprintln!("debug: average is {avg}");
    println!("average: {avg}");
}
average: 81

The program's normal output is that one line. The debug lines went somewhere else, to the terminal's error stream:

[src/main.rs:2:22] scores.iter().sum() = 245
[src/main.rs:3:5] total / scores.len() as u32 = 81
debug: average is 81

stdout and stderr

Every program has two output streams. stdout (standard output) is for the program's real output: what println! writes. stderr (standard error) is for errors, warnings and diagnostics: what eprintln! (and eprint!) and dbg! write. In a terminal both appear on screen, but they're separate:

$ cargo run > report.txt
[src/main.rs:2:22] scores.iter().sum() = 245
[src/main.rs:3:5] total / scores.len() as u32 = 81
debug: average is 81
$ cat report.txt
average: 81

> redirects only stdout to the file, so the debug lines didn't corrupt the report. The same goes when another program reads your output through a pipe. The tests on this site also compare only stdout, so debug lines on stderr never make a test fail.

A debugging routine

  1. Find where the output first goes wrong, and form a guess about why ("the cents are parsed wrong").
  2. Wrap the values that would confirm or rule out the guess in dbg!.
  3. Fix, rerun, then remove the dbg! calls. They print in release builds too. Teams often turn on clippy's dbg_macro lint in CI so a forgotten one can't be merged.

Common mistakes

Your task

Fill the blanks. line_total should log its result with the macro that prints an expression and its value (and still returns it), and the warning about the empty line belongs on stderr. The program's stdout must stay exactly total: 1097.

Challenge 2

This program should print total: $23.00 (12.50 + 3.50 + 7.00) but prints total: $22.55. Use dbg! inside parse_price to see which price is parsed wrong and why, then fix parse_price. You can leave dbg! calls in while you work: they print to stderr.

Challenge 3

Read lines of name,score from stdin. Lines that don't have a comma followed by a whole number are skipped, with a warning on stderr like warning: line 2: cannot read "bo,x". On stdout, print only:

valid: 2
average: 80.0

(the number of valid lines, then their average with one decimal, or average: none when there are no valid lines).

Next: Reading panics and backtraces