Some asserts are more computationally expensive than others, and I would like to have a way to enable/disable certain asserts. The asserts would be enabled/disabled depending on a compile-time constant. Conceptually this should be possible through the use of macros to simply omit code, but how can I do this in a clean way in Rust? (see below for my attempt that 'works' but seems messy)
For the sake of an example, I want to have code like this:
const MY_ASSERT_LEVEL = 1;
fn do_something_complicated(a: u32, b: u32) -> bool{
false
}
let a = 5;
let b = 10;
assert_simple!(a != b);
assert_heavy!(do_something_complicated(a, b));
I want to have assert_heavy
if MY_ASSERT_LEVEL
is at least 2, and similarly I want assert_simple
if MY_ASSERT_LEVEL
is at least 1. For the case MY_ASSERT_LEVEL == 0
, I want both asserts to be disabled.
My current solution is as follows:
I define in main.rs const MY_ASSERT_LEVEL: u32 = 2
; and then I write in a separate file my_asserts.rs
:
#[macro_export]
macro_rules! assert_simple {
($($arg:tt)*) => {
if $crate::MY_ASSERT_LEVEL >= 1 {
assert!($($arg)*);
}
};
macro_rules! assert_heavy {
($($arg:tt)*) => {
if $crate::MY_ASSERT_LEVEL >= 2 {
assert!($($arg)*);
}
};
}
And this 'works', but it seems messy for several reasons:
MY_ASSERT_LEVEL
is defined in the main file rather than in the same file as the assert macros. I tried moving MY_ASSERT_LEVEL
into the macro file, but the compiler tells me that the const MY_ASSERT_LEVEL
exists but is inaccessible to the macros.const MY ASSERT_LEVEL
) which has no role other than to define the macro levels. I would like to avoid this if possible.use crate::assert_simple
. Since in my use case I may have multiple assert macros, instead I would like to use some blanked statements like use my_assert_macros::*
but not sure how to get this functionality.I suspect I do not quite understand the macro syntax in Rust.
Can someone please help me? How I can implement the asserts I would like in a clean Rust way? I tried searching online to understand macros but this is as far as I could get.
This is easily done with the right file/module structure. Set your project up like this:
src
├─── main.rs
└─── my_asserts.rs
my_asserts.rs
pub(crate) const MY_ASSERT_LEVEL: u32 = 1;
macro_rules! assert_simple {
($($arg:tt)*) => {
if $crate::my_asserts::MY_ASSERT_LEVEL >= 1 {
assert!($($arg)*);
}
};
}
macro_rules! assert_heavy {
($($arg:tt)*) => {
if $crate::my_asserts::MY_ASSERT_LEVEL >= 2 {
assert!($($arg)*);
}
};
}
// these re-exports have to go after the macro definitions
pub(crate) use assert_heavy;
pub(crate) use assert_simple;
main.rs
use my_asserts::*;
mod my_asserts;
fn main() {
assert_simple!(true);
// this won't panic
assert_heavy!(false);
}