Hello,
I am learning Rust (which I love), and just discovered the dbg!(...)
macro for printing something with line number in debug mode for « quick and dirty » debugging.
That is all very nice and convenient, however from a usability point of view, I find it not always practical, as it still requires the object I want to print to implement the Debug
trait, and you may not always have it available, which would force you to add it, potentially in a large number of places just for the time of debugging.
Let me explain my example use case : I am trying to implement some (basic) AVL tree in rust, and would like to use dbg!(...)
to print the key and value of a node inside a deeply nested function to understand what is happening in my code at one point to fix a bug.
In the definition of my struct AVLTree <K, V>
, I do not require the Debug
trait for keys or values (and I do not wish to add this requirement, as it is unnecessary for the tree).
However, in my testcases, I use only types that do implement said trait (ie. Strings, str, numbers, etc.), so I thought I could use the macro in those cases and it would work, but it does not. I still cannot use the dbg!
macro, unless I change the definition of the whole struct, and its impl
block, and all the nested functions which all contain some re-definition of the type.
For example, I have a delete
function which also contains a delete_rec
, which itself contains a delete_leftmost_grandchild
(I don’t know if it is the optimal way to do what I am trying to do, but it is not very relevant now, and this case of nested functions might happen in « legitimate » use cases anyway), so to use dbg!
inside all that, I would have to alter the signature of every parent method to add a requirement to the Debug
trait for keys and values. I have something like that :
impl <K, V> AVLTree <K, V>
where K: Ord
{
fn delete(&mut self, key: &K) -> Option<V> {
fn delete_rec <K: Ord, V> (node: &mut Option <Box <AVLNode <K, V>>>, key: &K) -> Option<(V, i8)> {
match node.as_mut() {
... // (a bunch of code)
fn delete_leftmost_grandchild <K: Ord, V> (mut node: &mut Option <Box <AVLNode <K, V>>>) -> (V, i8) {
...
// I want to add a dbg! somewhere in this function. Something like that:
if let Some(real_node) = node {
dbg!(real_node.key);
dbg!(real_node.value);
... // etc.
}
And then, when I don’t need the debug macro anymore, I would need to remove it and change the definition of the structure and all the nested functions back so that they do not require the Debug
trait anymore, and the tree stays as « generic » and it can be !
This seems like a lot of boilerplate for a « quick and dirty » tool, and maybe there is a better solution that I do not know of ? For example, is there a way to only call the dbg!
macro only if the type implements Debug
, and silently ignore it (or display a warning) in the opposite case, without having to alter the definition of all containing functions ? Ie. something like :
if my_var implements Debug {
dbg!(my_var);
} else {
// Nothing
}
…and the compiler would automatically decide to include the dbg!
call depending on whether the type it is called on implements Debug
or not, without having to specify the trait requirement in all containing blocks and functions ?
Or maybe a debug macro that would do this test itself ? So that you could just do something like dbg_if_Debug!(my_var)
?
I guess the general idea would be to leave the type checking / trait implementation checking to the lowest possible level, and not require « parent » blocks and functions to be modified as well.
Maybe something like this already exists and I do not know of it ?
Thanks in advance.