Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Quick dbg!(expr) macro #2173

Closed
wants to merge 30 commits into from
Closed
Changes from 3 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
54ed527
rfc: quick_debug_macro, initial commit
Centril Oct 13, 2017
aa1968a
rfc: quick_debug_macro, improvements to the format
Centril Oct 13, 2017
fc99ed9
rfc: quick_debug_macro, s/rustbot/playbot-mini
Centril Oct 13, 2017
fe29054
rfc: quick_debug_macro, added unresolved question regarding non-Debug…
Centril Oct 13, 2017
b19274d
rfc: quick_debug_macro, s/production quality/non-trivial. See discuss…
Centril Oct 13, 2017
9bdeac5
rfc: quick_debug_macro, removed playbot-mini argument per discussion …
Centril Oct 13, 2017
a65370a
rfc: quick_debug_macro, optional formatting arg (req by @sdleffler) +…
Centril Oct 13, 2017
bff3335
rfc: quick_debug_macro, updated RFC with labels and multiple expressi…
Centril Oct 16, 2017
4499728
rfc, quick_debug_macro: fixed language wrt. while/without hurting perf
Centril Oct 18, 2017
550b95b
rfc, quick_debug_macro: inserted manual word-wrap for readability
Centril Oct 18, 2017
458c636
rfc, quick_debug_macro: exact impl now uses BufWriter + stderr().lock()
Centril Oct 18, 2017
193bf00
rfc, quick_debug_macro: readded column!() to currently unresolved + p…
Centril Oct 18, 2017
4769aec
rfc, quick_debug_macro: moved macro name bikeshed to # rationale & al…
Centril Oct 18, 2017
f4dfdd5
rfc, quick_debug_macro: added How do we teach this? section on pedago…
Centril Oct 18, 2017
34c1d05
rfc, quick_debug_macro: note on qdbg!
Centril Oct 18, 2017
be6b4d1
rfc, quick_debug_macro: trailing newline => unresolved questions.
Centril Oct 18, 2017
b081c82
rfc, quick_debug_macro: optimized macro for release builds wrt. compi…
Centril Oct 18, 2017
55b90ff
rfc, quick_debug_macro, ref-level-impl: notes on why macro is identit…
Centril Oct 18, 2017
34f8d6b
rfc, quick_debug_macro: fixed mistake in prev commit + fixed link in …
Centril Oct 18, 2017
08b5543
rfc, quick_debug_macro: expanded on dbg! as identity in motivation
Centril Oct 18, 2017
7307a6f
rfc, quick_debug_macro: enforce label is string literal + note on sho…
Centril Oct 18, 2017
ee0cc86
rfc, quick_debug_macro: updated to reflect discussed changes
Centril Oct 19, 2017
8fc21ec
rfc, quick_debug_macro: improved guide-level-explanation with factori…
Centril Oct 19, 2017
f98c6ad
rfc, quick_debug_macro: more Q & A + minor formatting fixes
Centril Oct 23, 2017
4c83cb5
rfc, quick_debug_macro: added notes on panic!()s
Centril Oct 24, 2017
708504e
rfc, quick_debug_macro: improved formatting + notes on specialization
Centril Oct 26, 2017
98a2492
rfc, quick_debug_macro: simplified macro slightly, no semantic changes
Centril Oct 27, 2017
d2d2f3b
rfc, quick_debug_macro: allow dbg!(); + internal consistency fixes + …
Centril Jan 19, 2018
354870c
rfc, quick_debug_macro: added motivation: the macro is not very usefu…
Centril Jan 19, 2018
5d0a0c9
rfc, quick_debug_macro: fixed typo; thanks @xftroxgpx
Centril Jan 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions text/0000-quick-debug-macro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
- Feature Name: quick_debug_macro
- Start Date: 2017-10-13
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

Adds a macro `dbg!(expr)` for quick and dirty `Debug`:ing of an expression to the terminal. The macro evaluates the expression, prints it to `STDERR`, and finally yields `expr`. On release builds, the macro is the identity function and has no side effects. The macro is added to the prelude of the standard library.

# Motivation
[motivation]: #motivation

The motivation to add this new `dbg!` macro is two-fold.

## For aspiring rustaceans

One of the often asked questions is how to print out variables to the terminal.
Delaying the need to explain formatting arguments in the statement `println!("{:?}", expr);` can help aspiring rustaceans to quickly learn the language. With `dbg!(expr);` there is no longer such need, which can be delayed until the developer actually cares about the format of the output and not just what value the expression evaluates to.

## For experienced developers

By using `dbg!(expr);`, the burden of a common papercut: writing `println!("{:?}", expr);` every time you want to see the evaluted-to value of an expression, can be significantly reduced. The developer no longer has to remember the formatting args and has to type significantly less (12 characters to be exact).

The shortness is also beneficial when asking `playbot-mini` on #[email protected] to evaluate and print an expression.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think IRC bots would rather a very different output format from what's described currently:

  • The [DEBUGGING, src/main.rs:1:12]: is clearly of no value there
  • Most bots only keep one or two lines (to avoid spamming), so {:#?} is worse than {:?}
  • The expression is one the line right above, so isn't really worth showing again

playbot (RIP) just used fn show<T:Debug>(x: T) -> T { print!("{:?}", x); x } which worked great for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I buy this =) I'll remove the argument from the RFC.


Centril marked this conversation as resolved.
Show resolved Hide resolved
To increase the utility of the macro, it acts as a pass-through function on the expression by simply printing it and then yielding it. On release builds, the macro is the identity function - thus, the macro can be used in release builds while hurting performance while also helping to debug the program.

## Why not use the `log` crate?

While the `log` crate offers a lot of utility, it first has to be used with `extern crate log;`. A logger then has to be set up before expressions can be logged. It is therefore not suitable for introducing newcommers to the language.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

## On debug builds:

First, some preliminaries:

```rust
#[derive(Debug)] // The type of expr in dbg!(expr) must be Debug.
struct Point {
x: usize,
y: usize,
}
```

With the following example (which most newcomers will benefit from):

```rust
fn main() {
dbg!(Point {
x: 1,
y: 2,
});

let p = Point {
x: 4,
y: 5,
};
dbg!(p);
}
```

The program will print the points to `STDERR` as:

```
[DEBUGGING, src/main.rs:1:4]:
=> Point{x: 1, y: 2,} = Point {
x: 1,
y: 2
}
[DEBUGGING, src/main.rs:7:4]:
=> p = Point {
x: 4,
y: 5
}
```

Here, `7:4` is the line and the column.

You may also save the debugged value to a variable or use it in an expression
since the debugging is pass-through:

```rust
fn main() {
let x = dbg!(1 + 2);
let y = dbg!(x + 1) + dbg!(3);
dbg!(y);
}
```

This prints the following to `STDERR`:

```
[DEBUGGING, src/main.rs:1:12]:
=> 1 + 2 = 3
[DEBUGGING, src/main.rs:2:12]:
=> x + 1 = 4
[DEBUGGING, src/main.rs:2:26]:
=> 3 = 3
[DEBUGGING, src/main.rs:3:4]:
=> y = 7
```

This way of using the macro will mostly benefit existing Rust programmers.

## On release builds:

The same examples above will print nothing to `STDERR` and will instead simply
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? dbg!() calls don't seem like something that you'd want to keep around other than when you're looking into a specific bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think looking into a specific bug is the most likely scenario. However...
There might be some fix you made, but you are not 100% sure, and you'd still like to include it into release builds. With the macro having no effect on release, this is possible.

Does this have any downsides?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spewing a bunch of stuff to stderr when a downstream crate isn't building with --release is a downside.

Asserts are a more common way of handling those kinds of checks IME.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I buy the argument regarding assert for at least libraries. However, it might have some value for programs?

evaluate the expressions.

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

The `dbg!` macro will be implemented as:

```rust
macro_rules! dbg {
($val: expr) => {
{
let tmp = $val;
if cfg!(debug_assertions) {
eprintln!("[DEBUGGING, {}:{}:{}]:\n=> {} = {:#?}",
file!(), line!(), column!(), stringify!($val), tmp );
}
tmp
}
}
}
```

Branching on `cfg!(debug_assertions)` means that if the program is built as a
release build, nothing will be printed, and the result of using the macro on
an expression is simply the expression itself. In effect the result is applying
the identity function on the expression, but the call will be inlined away such
that the overhead is zero.

The file name, line number and column is included for increased utility when included in production quality code. The expression is also stringified, so that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "production quality code" mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things ready to be included in programs & libraries run in production... This is arguably vague and subjective. It could be interpreted as things ready for cargo publish, but probably not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would argue that things that should be included in programs & libraries run in production should never be writing something like

[DEBUGGING, foobar.rs]
some-expr => 15

to stderr. If you want people to use your library, let them decide if and how they want to consume the debugging information you produce.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably only be included when developing/debugging a library, which the developer of the lib does.

Copy link
Member

@sfackler sfackler Oct 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "when included in production quality code" bit seems out of place if this wouldn't be used in production quality code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be reworded.

The intent is:

  • In the case of a library: If the library is used in production and you are the developer/contributor to it, and you want to make some changes and debug those, the macro should be useful to you when doing debug builds.
  • In the case of a program: The macro should be useful to you when doing debug builds and you shouldn't have to remove them in --release.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But how is the production-ness of the library relevant in the first case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed production quality to non-trivial per discussion here and on IRC.

the developer can easily see the syntactic structure of the expression that
evaluted to the RHS of the equality.

**NOTE:** The exact output format is not meant to be stabilized even when/if the
macro is stabilized.

# Drawbacks
[drawbacks]: #drawbacks

It could be considered bloat, and `println!("{:#?}", expr)` might be
sufficiently ergonomic for both experienced rustaceans and newcomers.

# Rationale and alternatives
[alternatives]: #alternatives

The formatting is informative, but could be formatted in other ways depending
on what is valued. A more terse format could be used if `stringify!` or `file!()` line and column numbers is not deemed beneficial, which this RFC argues it should.

The impact of not merging the RFC is that the papercut, if considered as such,
remains.

# Unresolved questions
[unresolved]: #unresolved-questions

The format used by the macro should be resolved prior to merging.
Some questions regarding the format are:

1. Should the `file!()` be included?
2. Should the line number be included?
3. Should the column number be included?
4. Should the `stringify!($val)` be included?

Other questions, which should also be resolved prior to merging, are:
5. Should the macro be pass-through with respect to the expression?
In other words: should the value of applying the macro to the expression be
the value of the expression?
6. Should the macro act as the identity function on release modes?
If the answer to this is yes, 5. must also be yes, i.e: 6. => 5.