26

In C++, I use something like this DEBUG macro:

#ifdef DEBUG
#define DEBUG_STDERR(x) (std::cerr << (x))
#define DEBUG_STDOUT(x) (std::cout << (x))
#else 
#define DEBUG_STDERR(x)
#define DEBUG_STDOUT(x)
#endif

Does Rust have something similar?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user3384741
  • 1,261
  • 3
  • 16
  • 21

4 Answers4

37

Rust 1.32.0

Rust 1.32.0 stabilized the dbg!() macro, which outputs:

  • The file name on which the macro was called.
  • The line number on which the macro was called.
  • A pretty print of the argument (which must implement the Debug trait).

Note: dbg!() moves its argument, so you may want to pass non-copy types by reference.

Example: Array of Points (Playground)

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let points = [
        Point { x: 0, y: 0 },
        Point { x: 2, y: 3 },
        Point { x: 5, y: 7 },
    ];

    dbg!(&points);
}

Program Output

[src/main.rs:14] &points = [
    Point {
        x: 0,
        y: 0
    },
    Point {
        x: 2,
        y: 3
    },
    Point {
        x: 5,
        y: 7
    }
]

Example: Conditional Compilation (Playground)

The OP expressed a desire to display the debug content only when compiling in debug mode.

The following is a way to achieve this:

#[cfg(debug_assertions)]
macro_rules! debug {
    ($x:expr) => { dbg!($x) }
}

#[cfg(not(debug_assertions))]
macro_rules! debug {
    ($x:expr) => { std::convert::identity($x) }
}

fn main() {
    let x = 4;
    debug!(x);
    if debug!(x == 5) {
        println!("x == 5");
    } else {
        println!("x != 5");
    }
}

Program Output (Debug Mode)

---------------------Standard Error-----------------------

[src/main.rs:13] x = 4
[src/main.rs:14] x == 5 = false

---------------------Standard Output----------------------

x != 5

Program Output (Release Mode)

---------------------Standard Output----------------------

x != 5

Before Rust 1.32.0

You could use the log crate or you could define one yourself.

ObliqueMotion
  • 483
  • 1
  • 5
  • 11
  • 1
    I've got rust 1.50 but even compiling with 'cargo build --release' I get the output? How to disable it in release mode? – MappaM Feb 19 '21 at 09:26
  • 1
    @MappaM This is expected as `dbg!` is [designed to be used in both Debug and Release builds](https://doc.rust-lang.org/std/macro.dbg.html). Official documentation is clear about its purpose: _Note that the macro is intended as a debugging tool and therefore you should avoid having uses of it in version control for long periods. Use cases involving debug output that should be added to version control are better served by macros such as `debug!` from the `log` crate._ – legends2k Aug 16 '21 at 05:42
  • As demonstrated, `dbg!` also returns its argument (which is why it has to move it) so it can be used to wrap any expression whatsoever (as long as its type is `Debug`). – BallpointBen Dec 12 '22 at 18:01
15

Although it makes sense to use something like the log crate as mentioned in DK's answer, here's how to do the direct equivalent of what you asked:

// The debug version
#[cfg(feature = "my_debug")]
macro_rules! debug_print {
    ($( $args:expr ),*) => { println!( $( $args ),* ); }
}

// Non-debug version
#[cfg(not(feature = "my_debug"))]
macro_rules! debug_print {
    ($( $args:expr ),*) => {}
}

fn main() {
    debug_print!("Debug only {}", 123);
}

And in your Cargo.toml, add a [features] section:

[features]
my_debug = []

The output then appears with cargo run --features my_debug, and doesn't with a plain cargo run.

Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
  • 9
    This can be a little more concise by piggybacking off of the existing `debug_assertions` attribute that controls macros such as `debug_assert!`. Just change the `#[cfg(feature = "my_debug")]` bits to `#[cfg(debug_assertions)]` and drop the change to `Cargo.toml`. This way the debug code is automatically ignored for release builds! – CJ McAllister Dec 05 '17 at 21:46
  • Nowadays this gives "error: macros that expand to items must be delimited with braces or followed by a semicolon" – MappaM Feb 19 '21 at 09:17
8

You could define them yourself, but it would be simpler to use the log crate, which defines several macros for various purposes (see the log documentation).

Note that the crate only provides the frontend for logging; you'll also need to select a backend. There's a basic example in the log documentation, or you could use something like env_logger or log4rs.

DK.
  • 55,277
  • 5
  • 189
  • 162
2

Macro based on Chris Emerson answer and CJ McAllister comment

// Disable warnings

#[allow(unused_macros)]

// The debug version

#[cfg(debug_assertions)]
macro_rules! log {
    ($( $args:expr ),*) => { println!( $( $args ),* ); }
}

// Non-debug version

#[cfg(not(debug_assertions))]
macro_rules! log {
    ($( $args:expr ),*) => {()}
}

Using

log!("Don't be crazy");
log!("Answer is {}", 42);

Building by cargo build --release will replace all log!(...) with unit tuple ();

I didn't found the way to replace to nothing but I think compiler will do it.