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
dbg!(scores.iter().sum())printed the sum and handed it tolet total. Wrapping the last line ofaveragedidn't change what the function returns.dbg!takes ownership of its argument, like passing it to a function. For aStringorVecyou still need afterwards, writedbg!(&name).dbg!(a, b)prints both.
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
- Find where the output first goes wrong, and form a guess about why ("the cents are parsed wrong").
- Wrap the values that would confirm or rule out the guess in
dbg!. - Fix, rerun, then remove the
dbg!calls. They print in release builds too. Teams often turn on clippy'sdbg_macrolint in CI so a forgotten one can't be merged.
Common mistakes
- Debug output on stdout: a stray
println!("here")changes the program's output and breaks anything reading it. Useeprintln!ordbg!. dbg!(s)and then usings:error[E0382]: borrow of moved value. Usedbg!(&s).- Printing everything at once. Print the few values that test your current guess; a wall of output hides the clue.
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).