rust-lang / rust

Empowering everyone to build reliable and efficient software.

Home Page:https://www.rust-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add a shorter macro for println!("{:?}", foo)

SimonSapin opened this issue · comments

When print-debugging, I find myself writing println!("{:?}", some expression) a lot. I wish I didn’t have to write the "{:?}" part, which is always the same. For temporary debugging-only code, I don’t care about making the output pretty, I only want to see what’s going on.

So I’d like to have a new macro added wherever println!() is defined:

macro_rules! dump(
    ($a: expr) => { println!("{:?}", $a) }
    ($a: expr, $b: expr) => { println!("{:?} {:?}", $a, $b) }
    ($a: expr, $b: expr, $c: expr) => { println!("{:?} {:?} {:?}", $a, $b, $c) }
    ($a: expr, $b: expr, $c: expr, $d: expr) => { println!("{:?} {:?} {:?} {:?}", $a, $b, $c, $d) }
)

… with this pattern repeated to however many arguments is appropriate. (Although, worst case, one can always pass a tuple.) Or is there a way to write this completely generic over the number of arguments?

You might be able to make it generic in the number of arguments with concat!. Writing it as a procedural macro would also do it.

You can make it generic with something like:

macro_rules! dump(                                                   
    ($($a:expr),*) => {                                              
        println!(concat!($(stringify!($a), " = {:?}, "),*), $($a),*);
    }                                                                
)                                                                    

We've seen some recent pushback from including more {:?} instances in the standard library, so this may not have a great place in there (see #11793). A macro like this would be great for beginners, however.

@alexcrichton that seems mostly orthogonal. Any opinion on whether this, a macro for quick print-style debugging, is something we want at all?

I personally feel that a small debugging macro is something that'll be home-grown on a per-repo basis. The hard part right now is that macro_rules is behind a feature gate. My own personal opinion is to not include something like this in libstd, but to certainly document it in the macro tutorial or in the documentation somewhere (just as an example).

Print-style debugging IMO should be quick, ugly, and temporary. In particular, "temporary" means that I do not want debugging-only code to lie around in my code for longer than a few change/compile/test cycles. It should never end up in version control.

Given this, an example macro in the tutorial is really not that helpful, as println!("{:?}", foo) is still quicker.

This is all personal opinions of course, but anything that is not in the prelude (or equivalent) doesn’t cut it for me.

nice, I have the same exact macro myself, except i add "{}:{}", file!(),line!(), ... and display the expressions eg "blah.rs:120 foo=... bar=... " etc. really handy ,as one can just click in the debug output and jump to a source location in some editors.

I was inspired by this issue to make a tiny library providing just such a macro that will also insert the file and line name, along with the expressions you are evaluating next to their results. Available here.

Example:

let a = 7;
let b = 4;
debug!(a, b, a + b);
// => file.rs - 3: a = 7, b = 4, a + b = 11

@reem that is pretty cool. I would be somewhat interested in seeing that in libdebug (although it would need a different name, to avoid conflicting with liblog's debug), I'm not sure if others agree.

How about inspect!, or perhaps dump!? I personally like inspect! a bit more, just because it sounds a bit more professional :).

tiny precedence: util.inspect from nodejs, though it also colorizes and pretty prints json

I'd be willing to make a PR if that's the right thing to do in this situation?

@reem, rather just than provide implementation, what’s needed now is convince the core team that this is a good idea. @alexcrichton disagrees, so far.

Personally, I think that even having to go back to the crate top-level to add extern crate debug; (assuming it’s not there permanently) whenever I want to use print-style debugging is too much overhead.

I tend to agree with you. I have started using this macro in my code for debugging and it is extremely useful, but it's pretty bothersome to have to add extern crates just to use it, then explicitly avoid checking those in to version control. Having it available by default would be great.

Given that {:?} now requires extern crate debug, I’d be in favor of just using "{}". It pushes users to implement Show on everything, but that’s probably a good thing.

@SimonSapin That's just more painful, to be honest. Now I'd like having a macro for this in the prelude even more.

To clarify: I meant having a macro in the prelude that uses expands to println!("{}", $a), println!("{} {}", $a, $b), etc.

Changes to the prelude now require a formal RFC. The {:?} formatter was moved to libdebug because it's known to be memory unsafe and will fundamentally prevent any kind of ABI stability. Adding a shortcut for it is going to be a hard sell since it's the opposite from the current direction.

@thestinger "memory unsafe", not safe, I'd assume :)

@sfackler: Yeah, here's one example I didn't fix when I was working on this before: #9869

I no longer have any interest in fixing the dozen remaining memory safety holes and dealing with the other breakage... and apparently no one else does, since it hasn't really been touched since then.

Given the direction {:?} is going, if I were to write an RFC for this I’d now propose generating {} instead. But I suspect that RFC would be postponed for the same reason as rust-lang/rfcs#163 was, so I won’t bother writing it just yet. If anyone else wants to, though, feel free.

I recently changed rust-inspect to use {} too.

Perhaps println!(), format!(), debug!() &c. could simply be extended to allow eliding the format string, making println!(foo, bar) equivalent to println!("{} {}", foo, bar). This has the slight disadvantage of silencing the error for println!(foo, bar) where foo is a string literal intended as a dynamic format string, and instead creating a bug at runtime. It would also be backwards-compatible, although still probably require an RFC.

+1 for making println!(foo, bar) equivalent to println!("{} {}", foo, bar) - would make things a lot less painful.

Yes, that sounds more likely to be accepted. Anyone wants to write up an RFC?

I submitted a PR doing that earlier in the year, it was rejected:
#12384

@seanmonstar Yes, this was requested a couple of times already, but for some reason it keeps getting rejected by the core team.
Maybe posting on Reddit and thus generating a wider debate on this would help?
@alexcrichton Has you opinion changed on this in any way?
Splitting this into a separate macro would defeat the purpose of this, I think.

I’ve renamed this macro show!, changed it to use {} (now that {:?} doesn’t exist anymore), and uploaded it to crates.io:

https://crates.io/crates/show
https://github.com/SimonSapin/rust-std-candidates#the-show-debugging-macro

I've published a crate called dump that extends the above #12015 (comment) to also print the type for each variable.

(Update: If anyone uses it, I am looking for someone to adopt the repo and crate, please email me.)

For those googling: dbg!() in the standard library is probably what you want