Logging and Debugging - Rust

Debugging and logging are two of the most crucial skills every Rustacean (a.k.a. Rust developer) needs to master! Whether you're tracking down a mysterious bug or leaving yourself notes in the form of logs, Rust has powerful tools to make the process smooth and efficient.

Debugging with println! - The Classic Approach

The simplest and most commonly used way to debug in Rust is by using println!. It's like writing sticky notes all over your code to track what's happening.

fn main() {
    let number = 42;
    println!("The answer is: {}", number);
}

Why use println!?

  • It's simple and works anywhere.
  • No extra setup needed.
  • Good for quick debugging.

Downside? You might accidentally leave println! all over your production code! Oops.

The Debug Trait {:?}

Rust provides a built-in way to print complex structures using Debug. Just slap {:?} in your println! and add #[derive(Debug)] to your struct.

#[derive(Debug)]
struct User {
    name: String,
    age: u8,
}

fn main() {
    let user = User {
        name: String::from("Alice"),
        age: 30,
    };
    println!("User info: {:?}", user);
}

Pro Tip: Use {:#?} for a pretty-printed version!

Using the log Crate for Proper Logging

If you need real logging (not just println! spam), Rust has the log crate, which provides different log levels:

  • trace! - Super detailed (for deep debugging).
  • debug! - Useful debug info.
  • info! - General app information.
  • warn! - Something seems fishy! 
  • error! - Major problem! 

Setting up log with env_logger

First, add this to Cargo.toml:

[dependencies]
log = "0.4"
env_logger = "0.10"

Then, initialize the logger:

use log::{info, warn, error};
use env_logger;

fn main() {
    env_logger::init();
    
    info!("Application has started!");
    warn!("This is a warning! Proceed with caution.");
    error!("Oh no! Something went wrong.");
}

Pro Tip: Run your program with RUST_LOG=info cargo run to see logs in action!

Debugging with dbg!() - The One-Liner Savior

The dbg!() macro is like println!, but even better!

fn main() {
    let x = 10;
    dbg!(x * 2);
}

Why use dbg!()?

  • Prints both the expression and its value.
  • Outputs to stderr, keeping stdout clean.
  • Automatically returns the value, so you can use it inline!

Using backtrace for Advanced Debugging

Sometimes, errors don't tell you where they happened. That's where backtraces come in handy!

First, enable backtracing:

RUST_BACKTRACE=1 cargo run

Then, use .unwrap() on an error-prone operation:

fn main() {
    let numbers = vec![1, 2, 3];
    println!("This will crash: {}", numbers[10]); // Out of bounds!
}

Pro Tip: If Rust crashes, RUST_BACKTRACE=full gives you even more juicy details!

Conclusion

println! - Good for quick checks. dbg!() - Great for debugging expressions.  log crate - Professional logging with log levels.  backtrace - Track down those sneaky crashes.

Post a Comment

0 Comments