Debug build only println macro?

hello, i would like to place print statements in my project but have it not be included in release builds.

any good options? not really looking for a outdated crate

Something like debug_print may be a bit old but it’s really only a very thin wrapper around the built in macros. Some crates are just “finished” and don’t necessarily need to be updated, so they’re not necessarily outdated.

If there are no better options (there may be) and you don’t want to use an external crate, you could always adopt similar code yourself - it’s really just a handful of lines.

(E.g. debug_print/src/lib.rs at master · RustyNixieTube/debug_print · GitHub for reference)

1 Like

One possibility is to use proper logging rather than just print statements. With the log crate you e.g. can use the debug!() macro to log a message at the debug level, the warn!() macro to log a message at the warning level, and so on.

Then you can decide what log level are actually printed either at run time (via environment variables or explicitly), or even at compile time so that all "log invocations at disabled levels will be skipped and will not even be present in the resulting binary".

From the log docs, here's how to "disable trace level logs in debug builds and trace, debug, and info level logs in release builds" by placing this snippet in your Cargo.toml:

[dependencies]
log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] }

env_logger and log are first and second in the debugging category, if that's something you find valuable and reassuring.

3 Likes

You could use the debug_assertions configuration option if it's the default value.

For example,

    #[cfg(debug_assertions)]
    println!("debug");
    #[cfg(not(debug_assertions))]
    println!("release");

You can of course guard a whole block or even a function with it, instead of a simple line of code.

Or you can use the macro to include the test in the code, like if cfg!(debug_assertions).

EDIT: I see that's the way debug_print (mentioned earlier) does it. Using debug_assertions directly allows you to discard additional code that you may require around your println! macro. Although the compiler is rather good at trimming unused code, usually. It has the other advantage of clearly marking the code used only for debugging/printing purpose.

what about "e" being warned to me in release builds? the e will be stripped even though im warned that its unused right?

            result = scan_task => {
                if let Err(e) = result {
                    debug_eprintln!("Scan task failed: {:?}", e);
                }
            },
            result = keep_alive_task => {
                if let Err(e) = result {
                    debug_eprintln!("Keep alive task failed: {:?}", e);
                }
            },
        }

You can suppress the warning by naming the variable with an underscore:

if let Err(_e) = result {
    debug_eprintln!("Scan task failed: {:?}", _e);
}

Or you could write a macro that expands to the whole if let Err(...) { ... } block in debug mode only.

but with the underscore too for warning suppression, the compiler will optimize out that block correct?

With or without the underscore, the whole if expression will be optimized out in release builds (where it has no effect) but not in debug builds (where it prints the error on failure).

The underscore only affects the compile-time unused variable warning. It doesn’t change how the code behaves at runtime.

1 Like

the macro could also include a let _ = e to suppress the unused variable lint

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.