Loading...
Loading...

Debugging Rust Programs

Debugging Rust applications requires understanding both compile-time errors and runtime issues. Rust's strong type system catches many bugs early, but when problems occur, you'll need tools like debug prints, logging, and debuggers to diagnose issues efficiently.

1. Solving Compiler Errors

Rust's compiler provides exceptionally helpful error messages. Learning to read them is your first debugging tool.

// Example error scenario
fn main() {
    let s = String::from("hello");
    print_string(s);
    println!("{}", s); // ERROR: value borrowed after move
}

fn print_string(s: String) {
    println!("{}", s);
}

Error Message Breakdown:

  • Clear explanation: "value used here after move"
  • Shows exact line numbers where move occurs
  • Suggests fixes: "consider cloning the value"

Tips:

  • Read messages from top to bottom
  • Look for help: annotations
  • Use cargo check for fast feedback

2. Print Debugging Techniques

Quick-and-dirty debugging with print statements remains useful in Rust.

// 1. Basic debug printing
println!("{:?}", some_value); // Requires Debug trait

// 2. Pretty-printing
println!("{:#?}", complex_struct);

// 3. Debug macros
dbg!(&some_var); // Prints file/line and value

// 4. Conditional debugging
#[cfg(debug_assertions)]
println!("[DEBUG] State: {:?}", state);

Best Practices:

  • Use dbg!() for temporary debugging (removed later)
  • Implement Debug trait for custom types
  • Add #[derive(Debug)] to structs/enums

3. Structured Logging

Production-grade debugging with log levels and structured data.

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

fn process_data(data: &Data) -> Result<(), Error> {
    debug!("Processing {:?}", data);
    
    if data.is_invalid() {
        warn!("Invalid data detected");
        return Err(Error::InvalidData);
    }
    
    info!("Processed {} records", data.len());
    Ok(())
}

// Initialize logger (in main)
env_logger::Builder::from_env(
    env_logger::Env::default().default_filter_or("info")
).init();

Logging Levels:

  • error! - Critical failures
  • warn! - Potential issues
  • info! - General operation
  • debug! - Diagnostic info
  • trace! - Verbose tracing

4. Using Debuggers

Step-through debugging with VS Code or command-line tools.

VS Code Setup:

  1. Install CodeLLDB extension
  2. Create .vscode/launch.json:
    {
        "version": "0.2.0",
        "configurations": [
            {
                "type": "lldb",
                "request": "launch",
                "name": "Debug",
                "program": "${workspaceFolder}/target/debug/my_app",
                "args": [],
                "cwd": "${workspaceFolder}"
            }
        ]
    }

Command Line:

# Build with debug symbols
cargo build

# Launch in GDB
gdb target/debug/my_app

# Basic GDB commands:
# break main.rs:10  # Set breakpoint
# run              # Start execution
# next             # Step over
# step             # Step into
# print x          # Inspect variable

5. Runtime Diagnostics

Advanced debugging tools for complex issues.

// 1. Backtraces on panic
RUST_BACKTRACE=1 cargo run

// 2. Memory debugging
#[cfg(debug_assertions)]
{
    use std::alloc::{GlobalAlloc, System};
    struct DebugAlloc;
    unsafe impl GlobalAlloc for DebugAlloc {
        unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
            println!("Allocating {} bytes", layout.size());
            System.alloc(layout)
        }
    }
    #[global_allocator]
    static ALLOCATOR: DebugAlloc = DebugAlloc;
}

// 3. Thread inspection
std::thread::current().id() // Get thread ID
std::thread::panicking()    // Check if panicking

Diagnostic Crates:

  • backtrace - Custom backtrace capture
  • tracing - Advanced instrumentation
  • valgrind - Memory/thread analysis (Linux)

6. Debug Build Configuration

Optimized debugging with Cargo profiles.

# Cargo.toml
[profile.dev]
opt-level = 0      # No optimization (better debug)
debug = true       # Include debug symbols
debug-assertions = true # Runtime checks

[profile.dev-override]
opt-level = 1      # Slightly optimized dependencies

# Environment variables
RUSTFLAGS="-C target-cpu=native" # CPU-specific debugging

Debug vs Release:

  • cargo build - Debug build (default)
  • cargo build --release - Optimized build
  • debug-assertions - Extra runtime checks in debug
0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home