From 54ed527387595ef6e35f52034c4287fe2609b329 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Fri, 13 Oct 2017 21:51:18 +0200 Subject: [PATCH 01/30] rfc: quick_debug_macro, initial commit --- text/0000-quick-debug-macro.md | 172 +++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 text/0000-quick-debug-macro.md diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md new file mode 100644 index 00000000000..112bb639a4c --- /dev/null +++ b/text/0000-quick-debug-macro.md @@ -0,0 +1,172 @@ +- 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 `rustbot` on #rust@irc.mozilla.org to evaluate and print an expression. + +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: + +``` +dbg(1:4) Point{x: 1, y: 2,} = Point { + x: 1, + y: 2 +} +dbg(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` as: + +``` +dbg(1:12) 1 + 2 = 3 +dbg(2:12) x + 1 = 4 +dbg(2:26) 3 = 3 +dbg(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 +evaluate the expressions. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The macro `dbg!` will be implemented as: + +```rust +macro_rules! dbg { + ($val: expr) => { + { + let tmp = $val; + if cfg!(debug_assertions) { + eprintln!("dbg({}:{}) {} = {:#?}", + 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 line number and column is included for increased utility when included in +production quality code. The expression is also stringified, so that 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 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 line number be included? +2. 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. \ No newline at end of file From aa1968a5d7a95b17bf49223fa3e41d20d9559263 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 13 Oct 2017 22:26:24 +0200 Subject: [PATCH 02/30] rfc: quick_debug_macro, improvements to the format --- text/0000-quick-debug-macro.md | 39 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 112bb639a4c..c0d048704a2 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -65,11 +65,13 @@ fn main() { The program will print the points to `STDERR` as: ``` -dbg(1:4) Point{x: 1, y: 2,} = Point { +[DEBUGGING, src/main.rs:1:4]: +=> Point{x: 1, y: 2,} = Point { x: 1, y: 2 } -dbg(7:4) p = Point { +[DEBUGGING, src/main.rs:7:4]: +=> p = Point { x: 4, y: 5 } @@ -88,13 +90,17 @@ fn main() { } ``` -This prints the following to `STDERR` as: +This prints the following to `STDERR`: ``` -dbg(1:12) 1 + 2 = 3 -dbg(2:12) x + 1 = 4 -dbg(2:26) 3 = 3 -dbg(3:4) y = 7 +[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. @@ -107,7 +113,7 @@ evaluate the expressions. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The macro `dbg!` will be implemented as: +The `dbg!` macro will be implemented as: ```rust macro_rules! dbg { @@ -115,8 +121,8 @@ macro_rules! dbg { { let tmp = $val; if cfg!(debug_assertions) { - eprintln!("dbg({}:{}) {} = {:#?}", - line!(), column!(), stringify!($val), tmp ); + eprintln!("[DEBUGGING, {}:{}:{}]:\n=> {} = {:#?}", + file!(), line!(), column!(), stringify!($val), tmp ); } tmp } @@ -130,9 +136,8 @@ 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 line number and column is included for increased utility when included in -production quality code. The expression is also stringified, so that the -developer can easily see the syntactic structure of the expression that +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 +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 @@ -148,8 +153,7 @@ sufficiently ergonomic for both experienced rustaceans and newcomers. [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 line and -column numbers is not deemed beneficial, which this RFC argues it should. +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. @@ -160,8 +164,9 @@ remains. The format used by the macro should be resolved prior to merging. Some questions regarding the format are: -1. Should the line number be included? -2. Should the column number be included? +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: From fc99ed99f586fc444abfe9ec5641b930e6d84459 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 13 Oct 2017 22:56:39 +0200 Subject: [PATCH 03/30] rfc: quick_debug_macro, s/rustbot/playbot-mini --- text/0000-quick-debug-macro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index c0d048704a2..462bcb79ef0 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -22,7 +22,7 @@ Delaying the need to explain formatting arguments in the statement `println!("{: 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 `rustbot` on #rust@irc.mozilla.org to evaluate and print an expression. +The shortness is also beneficial when asking `playbot-mini` on #rust@irc.mozilla.org to evaluate and print an expression. 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. From fe29054df6463451914406e89bb3ece6a07b4e54 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 13 Oct 2017 23:36:11 +0200 Subject: [PATCH 04/30] rfc: quick_debug_macro, added unresolved question regarding non-Debug types --- text/0000-quick-debug-macro.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 462bcb79ef0..d280d4159ec 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -174,4 +174,10 @@ Other questions, which should also be resolved prior to merging, are: 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. \ No newline at end of file + If the answer to this is yes, 5. must also be yes, i.e: 6. => 5. + +To be revisited once [`specialization`](https://github.com/rust-lang/rfcs/pull/1210) +has been stabilized: + +7. Should expressions and values of non-`Debug` types be usable with this macro +by using `std::intrinsics::type_name` for such types and the `Debug` impl for `T : Debug` types as done in version 0.1.2 of [`debugit` ](https://docs.rs/debugit/0.1.2/debugit/)? This depends on specialization. \ No newline at end of file From b19274d426ff4099229291de11a51a8b17742318 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 13 Oct 2017 23:43:46 +0200 Subject: [PATCH 05/30] rfc: quick_debug_macro, s/production quality/non-trivial. See discussion with @sfackler --- text/0000-quick-debug-macro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index d280d4159ec..2127a4ab7ee 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -136,7 +136,7 @@ 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 +The file name, line number and column is included for increased utility when included in non-trivial code. The expression is also stringified, so that the developer can easily see the syntactic structure of the expression that evaluted to the RHS of the equality. From 9bdeac5f9b5e9873fdc4e6497c44ea222a363176 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 13 Oct 2017 23:48:58 +0200 Subject: [PATCH 06/30] rfc: quick_debug_macro, removed playbot-mini argument per discussion with scottmcm --- text/0000-quick-debug-macro.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 2127a4ab7ee..7fe272b7d70 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -22,8 +22,6 @@ Delaying the need to explain formatting arguments in the statement `println!("{: 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 #rust@irc.mozilla.org to evaluate and print an expression. - 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? From a65370a25d1fcd8189093a9eb893e33781e6bbdd Mon Sep 17 00:00:00 2001 From: Mazdak Date: Sat, 14 Oct 2017 00:19:22 +0200 Subject: [PATCH 07/30] rfc: quick_debug_macro, optional formatting arg (req by @sdleffler) + notes on bikeshed --- text/0000-quick-debug-macro.md | 46 +++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 7fe272b7d70..220779e61de 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -28,6 +28,21 @@ To increase the utility of the macro, it acts as a pass-through function on the 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. +## Bikeshed: The name of the macro + +Several names has been proposed for the macro. Some of the candidates were: + ++ `debug!`, which was the original name. This was however already used by the `log` +crate. ++ `d!`, which was deemded to be too short to be informative and convey intent. ++ `dump!`, which was confused with stack traces. ++ `show!`, inspired by Haskell. `show` was deemed less obvious than `dbg!`. ++ `peek!`, which was also deemed less obvious. ++ `DEBUG!`, which was deemed too screamy. + +While it is unfortunate that `debug!` was unavailable, `dbg!` was deemed the +next best thing, which is why it was picked as the name of the macro. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -103,6 +118,24 @@ This prints the following to `STDERR`: This way of using the macro will mostly benefit existing Rust programmers. +Optionally, the default formatting used by the macro can be changed by +passing in a format as a second argument as done in: + +```rust +fn main() { + let p = dbg!(Point { x: 4, y: 5 }, "{:?}"); +} +``` + +This prints the following to `STDERR`: + +``` +Point { x: 4, y: 5 } +``` + +In this case, the main advantage of `dbg` over `println` in this case is that +the value of the expression is passed back to the "caller" of the macro. + ## On release builds: The same examples above will print nothing to `STDERR` and will instead simply @@ -120,7 +153,16 @@ macro_rules! dbg { let tmp = $val; if cfg!(debug_assertions) { eprintln!("[DEBUGGING, {}:{}:{}]:\n=> {} = {:#?}", - file!(), line!(), column!(), stringify!($val), tmp ); + file!(), line!(), column!(), stringify!($val), tmp); + } + tmp + } + }; + ($val: expr, $fmt: expr) => { + { + let tmp = $val; + if cfg!(debug_assertions) { + eprintln!($fmt, tmp); } tmp } @@ -141,6 +183,8 @@ 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. +By passing in a format as the second optional argument to the macro, the way the value is printed can be changed. This is done in `($val: expr, $fmt: expr)`. + # Drawbacks [drawbacks]: #drawbacks From bff33351deecd7f6701ed3a09a5ce15a030dcf24 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Mon, 16 Oct 2017 04:48:58 +0200 Subject: [PATCH 08/30] rfc: quick_debug_macro, updated RFC with labels and multiple expressions according to https://github.com/rust-lang/rfcs/pull/2173#issuecomment-336752562 --- text/0000-quick-debug-macro.md | 312 ++++++++++++++++++++++++++++----- 1 file changed, 267 insertions(+), 45 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 220779e61de..f250639dfca 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -6,7 +6,7 @@ # 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. +Adds a macro `dbg!(expr1 [, expr2, .., exprN])` for quick and dirty `Debug`:ing of expressions to the terminal. The macro evaluates expressions, prints it to `STDERR`, and finally yields a flat tuple of `(expr1 [, expr2, .. exprN])`. 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 @@ -24,6 +24,9 @@ By using `dbg!(expr);`, the burden of a common papercut: writing `println!("{:?} 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. +Additionally, by allowing the user to pass in multiple expressions and label +them, the utility is further augmented. + ## 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. @@ -46,7 +49,7 @@ next best thing, which is why it was picked as the name of the macro. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -## On debug builds: +## On debug builds First, some preliminaries: @@ -58,7 +61,7 @@ struct Point { } ``` -With the following example (which most newcomers will benefit from): +With the following example, which most newcomers will benefit from: ```rust fn main() { @@ -93,7 +96,7 @@ The program will print the points to `STDERR` as: 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: +since the debugging is pass-through. This is seen in the following example: ```rust fn main() { @@ -116,27 +119,79 @@ This prints the following to `STDERR`: => y = 7 ``` -This way of using the macro will mostly benefit existing Rust programmers. +More expressions may be debugged in one invocation of the macro, as seen in the following example: +```rust +fn main() { + let a = 1; + let b = 2; + let _ : u32 = dbg!(a); + let _ : (u32, u32) = dbg!(a, b); + let _ : (u32, u32, u32) = dbg!(a, b, a + b); + + let p = Point { x: 4, y: 5 }; + let q = Point { x: 2, y: 1 }; + let qp : (&Point, &Point) = dbg!(&p, &q); +} +``` + +As seen in the example, the type of the expression `dbg!(expr)` is the type of `expr`. For `dbg!(expr1, expr2 [, .., exprN])` the type is that of the tuple `(expr1, expr2 [, .., exprN])`. + +The example above prints the following to `STDERR`: +``` +[DEBUGGING, src/main.rs:3:18]: +=> a = 1 +[DEBUGGING, src/main.rs:4:25]: +=> a = 1, b = 2 +[DEBUGGING, src/main.rs:5:30]: +=> a = 1, b = 2, a + b = 3 +[DEBUGGING, src/main.rs:9:31]: +=> &p = Point { + x: 4, + y: 5 +}, &q = Point { + x: 2, + y: 1 +} +``` -Optionally, the default formatting used by the macro can be changed by -passing in a format as a second argument as done in: +Furthermore, instead of using `stringify!` on the expressions, which is done by default, the user may provide labels, as done in: ```rust fn main() { - let p = dbg!(Point { x: 4, y: 5 }, "{:?}"); + let w = 1; + let h = 2; + dbg!("width" => w, "height" => h, "area" => w * h); + + let p = Point { x: 4, y: 5 }; + let q = Point { x: 2, y: 1 }; + dbg!("first point" => &p, "second point" => &p); } ``` -This prints the following to `STDERR`: - +This allows the user to provide more descriptive names if necessary. With this example, the following is printed to `STDERR`: ``` -Point { x: 4, y: 5 } +[DEBUGGING, src/main.rs:2:4]: +=> "width" = 1, "height" = 2, "area" = 2 +[DEBUGGING, src/main.rs:7:4]: +=> "first point" = Point { + x: 4, + y: 5 +}, "second point" = Point { + x: 2, + y: 1 +} ``` -In this case, the main advantage of `dbg` over `println` in this case is that -the value of the expression is passed back to the "caller" of the macro. +The ways of using the macro used in later (not the first) examples will mostly benefit existing Rust programmers. + +### Omitting the source location + +Those developers who feel the source location header is overly verbose may +choose to opt-out by setting the environment variable `RUST_DBG_NO_LOCATION` to +`"0"`. This is a one-time setup cost the developer has to make for all current +and future Rust projects. -## On release builds: +## On release builds The same examples above will print nothing to `STDERR` and will instead simply evaluate the expressions. @@ -144,46 +199,204 @@ evaluate the expressions. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The `dbg!` macro will be implemented as: +The macro is called `dbg` and accepts either a non-empty comma-separated or +comma-terminated list of `expr`, or a non-empty list of `label => expr` which +is also separated or terminated with commas. + +The terminated versions are defined as: + +1. `($($val: expr),+,) => { dbg!( $($val),+ ) };` +2. `($($lab: expr => $val: expr),+,) => { dbg!( $($lab => $val),+ ) };` +The separated versions accept the following: + +1. `($($val: expr),+)` +2. `($($lab: expr => $val: expr),+)` + +The macro only prints something if `cfg!(debug_assertions)` holds, meaning that +if the program is built as a release build, nothing will be printed, and the result of using the macro on an expressions or expressions is simply the expression +itself or a flat tuple of the expressions themselves. In effect the result is applying the identity function on the expression(s), but the call will be inlined away such that the overhead is zero. + +## The type of `dbg!(expressions)` + +"Applying" `dbg` on a non-empty list of expressions `expr1 [, expr2 [, .., exprN])` gives back an expression of the following type and value: + ++ List of size 1, `dbg!(expr)`: The type is the type of `expr` and the value is +the value of `expr`. + ++ Otherwise, `dbg!(expr1, expr2 [, expr3, .., exprN])`: The type is the type +of the tuple `(expr1, expr2 [, expr3, .., exprN])` which is the value. + +## Schematic/step-wise explanation + +1. Assume `let p = option_env!("RUST_DBG_NO_LOCATION").map_or_else(|| true, |s| s == "0");`. If `p` holds, the file name (given by `file!()`), line number (`line!()`) and column (`column!()`) is included in the print out for increased utility when the macro is used in non-trivial code. This is wrapped by `[DEBUGGING, ]:` as in: ```rust -macro_rules! dbg { - ($val: expr) => { - { - let tmp = $val; - if cfg!(debug_assertions) { - eprintln!("[DEBUGGING, {}:{}:{}]:\n=> {} = {:#?}", - file!(), line!(), column!(), stringify!($val), tmp); - } - tmp - } +eprintln!("[DEBUGGING, {}:{}:{}]:", file!(), line!(), column!()); +``` +If `p` does not hold, this step prints nothing. + +2. An arrow is then printed on the next line: `eprint!("=> ");`. + +3. + For `($($val: expr),+)` + +For each `$val` (the expression), the following is printed, comma separated: +The value of the expression is presented on the right hand side (RHS) of an equality sign `=` while the result of `stringify!(expr)` is presented on the +left hand side (LHS). This is done so that the developer easily can see the syntactic structure of the expression that evaluted to RHS. + +In other words, the following: `eprint!("{} = {:#?}", stringify!($lab), tmp);`. + +3. + For `($($lab: expr => $val: expr),+)`: + +For each `$lab => $val` (the label and expression), the following is printed, +comma separated: The value of the expression is presented on RHS of an equality sign `=` while the label is presented on LHS. + +In other words, the following: `eprint!("{} = {:#?}", stringify!($lab), tmp);`. + +**NOTE:** The label is only guaranteed when it is a string slice literal. + +**NOTE:** The exact output format is not meant to be stabilized even when/if the +macro is stabilized. + +## Example implementation + +The `dbg!` macro is semantically (with the notable detail that the helper macros and any non-`pub` `fn`s must be inlined in the actual implementation): + +```rust +macro_rules! cfg_dbg { + ($($x:tt)+) => { + if cfg!(debug_assertions) { $($x)* } }; - ($val: expr, $fmt: expr) => { - { - let tmp = $val; - if cfg!(debug_assertions) { - eprintln!($fmt, tmp); +} + +fn dbg_with_location() -> bool { + option_env!("RUST_DBG_NO_LOCATION").map_or_else(|| true, |s| s == "0") +} + +macro_rules! dbg_header { + ($expr: expr) => {{ + cfg_dbg! { + if dbg_with_location() { + eprintln!("[DEBUGGING, {}:{}:{}]:", file!(), line!(), column!()); } - tmp + eprint!("=> "); } + let ret = $expr; + cfg_dbg! { eprintln!(""); } + ret + }}; +} + +macro_rules! dbg_comma { + ($valf: expr) => { $valf }; + ($valf: expr, $($val: expr),+) => { + ( $valf, $({ cfg_dbg! { eprint!(", "); } $val}),+ ) } } + +macro_rules! dbg_term { + ($val: expr) => { dbg_term!($val => $val) }; + ($lab: expr => $val: expr) => {{ + let tmp = $val; + cfg_dbg! { eprint!("{} = {:#?}", stringify!($lab), tmp); } + tmp + }}; +} + +#[macro_export] +macro_rules! dbg { + ($($val: expr),+,) => { + dbg!( $($val),+ ) + }; + ($($lab: expr => $val: expr),+,) => { + dbg!( $($lab => $val),+ ) + }; + ($($val: expr),+) => { + dbg_header!(dbg_comma!($(dbg_term!($val)),+)) + }; + ($($lab: expr => $val: expr),+) => { + dbg_header!(dbg_comma!($(dbg_term!($lab => $val)),+)) + }; +} ``` -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. +## Exact implementation -The file name, line number and column is included for increased utility when included in non-trivial code. The expression is also stringified, so that -the developer can easily see the syntactic structure of the expression that -evaluted to the RHS of the equality. +The exact implementation is given by: -**NOTE:** The exact output format is not meant to be stabilized even when/if the -macro is stabilized. +```rust +// For #[allow(unused_parens)]: +#![feature(stmt_expr_attributes)] + +#[macro_export] +macro_rules! dbg { + // Handle trailing comma: + ($($val: expr),+,) => { + dbg!( $($val),+ ) + }; + ($($lab: expr => $val: expr),+,) => { + dbg!( $($lab => $val),+ ) + }; + // Without label, use source of $val as label: + ($($val: expr),+) => { + dbg!($($val => $val),+) + }; + // With label: + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { + #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] + { + if cfg!(debug_assertions) { + // Print out source location unless silenced by setting + // the env var RUST_DBG_NO_LOCATION != 0. + let p = option_env!("RUST_DBG_NO_LOCATION") + .map_or_else(|| true, |s| s == "0"); + if p { + eprintln!("[DEBUGGING, {}:{}:{}]:", + file!(), line!(), column!()); + } + // Print out arrow (on a new line): + eprint!("=> "); + } -By passing in a format as the second optional argument to the macro, the way the value is printed can be changed. This is done in `($val: expr, $fmt: expr)`. + // Foreach label and expression: + // 1. Evaluate each expression to value, + // 2. Print out $lab = value + // Separate with comma. + let ret = ( + { + // Evaluate, tmp is value: + let tmp = $valf; + // Print out $lab = tmp: + if cfg!(debug_assertions) { + eprint!("{} = {:#?}", stringify!($labf), tmp); + } + // Yield tmp: + tmp + } + $(, { + // Comma separator: + if cfg!(debug_assertions) { eprint!(", "); } + { + // Evaluate, tmp is value: + let tmp = $val; + // Print out $lab = tmp: + if cfg!(debug_assertions) { + eprint!("{} = {:#?}", stringify!($lab), tmp); + } + // Yield tmp: + tmp + } + } )* + ); + + // Newline: + if cfg!(debug_assertions) { eprintln!(""); } + + // Return the expression: + ret + } + }; +} +``` # Drawbacks [drawbacks]: #drawbacks @@ -195,7 +408,8 @@ sufficiently ergonomic for both experienced rustaceans and newcomers. [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. +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 RFC argues that the possibility of opting out to this header via an env var strikes a good balance. The impact of not merging the RFC is that the papercut, if considered as such, remains. @@ -204,20 +418,28 @@ remains. [unresolved]: #unresolved-questions The format used by the macro should be resolved prior to merging. -Some questions regarding the format are: + +## Formerly unresolved + +Some questions regarding the format were: 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: +Other questions, which should also be resolved prior to merging, were: + 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. +They have all been answered in the affirmative. + +## Currently unresolved + To be revisited once [`specialization`](https://github.com/rust-lang/rfcs/pull/1210) has been stabilized: From 4499728067992c2972123973f288a4392c364c62 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 14:47:01 +0200 Subject: [PATCH 09/30] rfc, quick_debug_macro: fixed language wrt. while/without hurting perf --- text/0000-quick-debug-macro.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index f250639dfca..7761829c9c7 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -22,7 +22,9 @@ Delaying the need to explain formatting arguments in the statement `println!("{: 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). -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. +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 without hurting performance while allowing the debugging of the program in debug +builds. Additionally, by allowing the user to pass in multiple expressions and label them, the utility is further augmented. From 550b95bed43ef382d4421a90256c4956a69c70cc Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 18:28:23 +0200 Subject: [PATCH 10/30] rfc, quick_debug_macro: inserted manual word-wrap for readability --- text/0000-quick-debug-macro.md | 98 +++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 7761829c9c7..dcd3ee0851d 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -6,7 +6,11 @@ # Summary [summary]: #summary -Adds a macro `dbg!(expr1 [, expr2, .., exprN])` for quick and dirty `Debug`:ing of expressions to the terminal. The macro evaluates expressions, prints it to `STDERR`, and finally yields a flat tuple of `(expr1 [, expr2, .. exprN])`. 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. +Adds a macro `dbg!(expr1 [, expr2, .., exprN])` for quick and dirty `Debug`:ing +of expressions to the terminal. The macro evaluates expressions, prints it to +`STDERR`, and finally yields a flat tuple of `(expr1 [, expr2, .. exprN])`. +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 @@ -14,16 +18,26 @@ Adds a macro `dbg!(expr1 [, expr2, .., exprN])` for quick and dirty `Debug`:ing The motivation to add this new `dbg!` macro is two-fold. ## For aspiring rustaceans +[for-aspiring-rustaceans]: #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. +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). +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). -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 without hurting performance while allowing the debugging of the program in debug +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 +without hurting performance while allowing the debugging of the program in debug builds. Additionally, by allowing the user to pass in multiple expressions and label @@ -31,14 +45,16 @@ them, the utility is further augmented. ## 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. +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. ## Bikeshed: The name of the macro Several names has been proposed for the macro. Some of the candidates were: -+ `debug!`, which was the original name. This was however already used by the `log` -crate. ++ `debug!`, which was the original name. This was however already used by the +`log` crate. + `d!`, which was deemded to be too short to be informative and convey intent. + `dump!`, which was confused with stack traces. + `show!`, inspired by Haskell. `show` was deemed less obvious than `dbg!`. @@ -121,7 +137,8 @@ This prints the following to `STDERR`: => y = 7 ``` -More expressions may be debugged in one invocation of the macro, as seen in the following example: +More expressions may be debugged in one invocation of the macro, as seen in the +following example: ```rust fn main() { let a = 1; @@ -136,7 +153,9 @@ fn main() { } ``` -As seen in the example, the type of the expression `dbg!(expr)` is the type of `expr`. For `dbg!(expr1, expr2 [, .., exprN])` the type is that of the tuple `(expr1, expr2 [, .., exprN])`. +As seen in the example, the type of the expression `dbg!(expr)` is the type of +`expr`. For `dbg!(expr1, expr2 [, .., exprN])` the type is that of the tuple +`(expr1, expr2 [, .., exprN])`. The example above prints the following to `STDERR`: ``` @@ -156,7 +175,8 @@ The example above prints the following to `STDERR`: } ``` -Furthermore, instead of using `stringify!` on the expressions, which is done by default, the user may provide labels, as done in: +Furthermore, instead of using `stringify!` on the expressions, which is done by +default, the user may provide labels, as done in: ```rust fn main() { @@ -170,7 +190,8 @@ fn main() { } ``` -This allows the user to provide more descriptive names if necessary. With this example, the following is printed to `STDERR`: +This allows the user to provide more descriptive names if necessary. With this +example, the following is printed to `STDERR`: ``` [DEBUGGING, src/main.rs:2:4]: => "width" = 1, "height" = 2, "area" = 2 @@ -184,7 +205,8 @@ This allows the user to provide more descriptive names if necessary. With this e } ``` -The ways of using the macro used in later (not the first) examples will mostly benefit existing Rust programmers. +The ways of using the macro used in later (not the first) examples will mostly +benefit existing Rust programmers. ### Omitting the source location @@ -216,12 +238,17 @@ The separated versions accept the following: 2. `($($lab: expr => $val: expr),+)` The macro only prints something if `cfg!(debug_assertions)` holds, meaning that -if the program is built as a release build, nothing will be printed, and the result of using the macro on an expressions or expressions is simply the expression -itself or a flat tuple of the expressions themselves. In effect the result is applying the identity function on the expression(s), but the call will be inlined away such that the overhead is zero. +if the program is built as a release build, nothing will be printed, and the +result of using the macro on an expressions or expressions is simply the +expression itself or a flat tuple of the expressions themselves. In effect the +result is applying the identity function on the expression(s), but the call will +be inlined away such that the overhead is zero. ## The type of `dbg!(expressions)` -"Applying" `dbg` on a non-empty list of expressions `expr1 [, expr2 [, .., exprN])` gives back an expression of the following type and value: +"Applying" `dbg` on a non-empty list of expressions +`expr1 [, expr2 [, .., exprN])` gives back an expression of the following type +and value: + List of size 1, `dbg!(expr)`: The type is the type of `expr` and the value is the value of `expr`. @@ -231,10 +258,17 @@ of the tuple `(expr1, expr2 [, expr3, .., exprN])` which is the value. ## Schematic/step-wise explanation -1. Assume `let p = option_env!("RUST_DBG_NO_LOCATION").map_or_else(|| true, |s| s == "0");`. If `p` holds, the file name (given by `file!()`), line number (`line!()`) and column (`column!()`) is included in the print out for increased utility when the macro is used in non-trivial code. This is wrapped by `[DEBUGGING, ]:` as in: +1. Assume +`let p = option_env!("RUST_DBG_NO_LOCATION").map_or(true, |s| s == "0");`. +If `p` holds, the file name (given by `file!()`), line number (`line!()`) and +column (`column!()`) is included in the print out for increased utility when the +macro is used in non-trivial code. This is wrapped by `[DEBUGGING, ]:` +as in: + ```rust eprintln!("[DEBUGGING, {}:{}:{}]:", file!(), line!(), column!()); ``` + If `p` does not hold, this step prints nothing. 2. An arrow is then printed on the next line: `eprint!("=> ");`. @@ -242,26 +276,30 @@ If `p` does not hold, this step prints nothing. 3. + For `($($val: expr),+)` For each `$val` (the expression), the following is printed, comma separated: -The value of the expression is presented on the right hand side (RHS) of an equality sign `=` while the result of `stringify!(expr)` is presented on the -left hand side (LHS). This is done so that the developer easily can see the syntactic structure of the expression that evaluted to RHS. +The value of the expression is presented on the right hand side (RHS) of an +equality sign `=` while the result of `stringify!(expr)` is presented on the +left hand side (LHS). This is done so that the developer easily can see the +syntactic structure of the expression that evaluted to RHS. In other words, the following: `eprint!("{} = {:#?}", stringify!($lab), tmp);`. 3. + For `($($lab: expr => $val: expr),+)`: For each `$lab => $val` (the label and expression), the following is printed, -comma separated: The value of the expression is presented on RHS of an equality sign `=` while the label is presented on LHS. +comma separated: The value of the expression is presented on RHS of an equality +sign `=` while the label is presented on LHS. In other words, the following: `eprint!("{} = {:#?}", stringify!($lab), tmp);`. -**NOTE:** The label is only guaranteed when it is a string slice literal. +**NOTE:** The label is only guaranteed to work when it is a string slice literal. **NOTE:** The exact output format is not meant to be stabilized even when/if the macro is stabilized. ## Example implementation -The `dbg!` macro is semantically (with the notable detail that the helper macros and any non-`pub` `fn`s must be inlined in the actual implementation): +The `dbg!` macro is semantically (with the notable detail that the helper macros +and any non-`pub` `fn`s must be inlined in the actual implementation): ```rust macro_rules! cfg_dbg { @@ -410,8 +448,10 @@ sufficiently ergonomic for both experienced rustaceans and newcomers. [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 RFC argues that the possibility of opting out to this header via an env var strikes a good balance. +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 RFC argues that the possibility of opting out to this +header via an env var strikes a good balance. The impact of not merging the RFC is that the papercut, if considered as such, remains. @@ -442,8 +482,14 @@ They have all been answered in the affirmative. ## Currently unresolved -To be revisited once [`specialization`](https://github.com/rust-lang/rfcs/pull/1210) +[`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 + +To be revisited once [`specialization`] has been stabilized: +[`debugit`]: https://docs.rs/debugit/0.1.2/debugit/ + 7. Should expressions and values of non-`Debug` types be usable with this macro -by using `std::intrinsics::type_name` for such types and the `Debug` impl for `T : Debug` types as done in version 0.1.2 of [`debugit` ](https://docs.rs/debugit/0.1.2/debugit/)? This depends on specialization. \ No newline at end of file +by using `std::intrinsics::type_name` for such types and the `Debug` impl for +`T : Debug` types as done in version 0.1.2 of [`debugit`]? This depends on +specialization. \ No newline at end of file From 458c6365fc6a8707d7c86e21f03905808268c23c Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 18:32:18 +0200 Subject: [PATCH 11/30] rfc, quick_debug_macro: exact impl now uses BufWriter + stderr().lock() --- text/0000-quick-debug-macro.md | 51 ++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index dcd3ee0851d..1031a0e3168 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -376,7 +376,7 @@ macro_rules! dbg { ($($lab: expr => $val: expr),+,) => { dbg!( $($lab => $val),+ ) }; - // Without label, use source of $val as label: + // Without label, use source of $val: ($($val: expr),+) => { dbg!($($val => $val),+) }; @@ -384,43 +384,61 @@ macro_rules! dbg { ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { + // DEBUG: Lock STDERR in a buffered writer. + // Motivation: + // 1. to avoid needless re-locking of STDERR at every write(ln)!. + // 2. to ensure that the printed message is not interleaved, which + // would disturb the readability of the output, by other messages to + // STDERR. + use ::std::io::Write; + #[cfg(debug_assertions)] + let stderr = ::std::io::stderr(); + #[cfg(debug_assertions)] + let mut err = ::std::io::BufWriter::new(stderr.lock()); + + // RELEASE: satisfy the type system with a Writer that's never used. + #[cfg(not(debug_assertions))] + let mut err = ::std::io::sink(); + if cfg!(debug_assertions) { - // Print out source location unless silenced by setting - // the env var RUST_DBG_NO_LOCATION != 0. + // Print out source location unless silenced: let p = option_env!("RUST_DBG_NO_LOCATION") - .map_or_else(|| true, |s| s == "0"); + .map_or(true, |s| s == "0"); if p { - eprintln!("[DEBUGGING, {}:{}:{}]:", - file!(), line!(), column!()); + writeln!(&mut err, "[DEBUGGING, {}:{}:{}]:", + file!(), line!(), column!()).unwrap(); } // Print out arrow (on a new line): - eprint!("=> "); + write!(&mut err, "=> ").unwrap(); } // Foreach label and expression: - // 1. Evaluate each expression to value, - // 2. Print out $lab = value - // Separate with comma. + // 1. Evaluate each expression, + // 2. Print out $lab = value of expression let ret = ( { // Evaluate, tmp is value: let tmp = $valf; // Print out $lab = tmp: if cfg!(debug_assertions) { - eprint!("{} = {:#?}", stringify!($labf), tmp); + write!(&mut err, "{} = {:#?}", stringify!($labf), tmp) + .unwrap(); } // Yield tmp: tmp } $(, { // Comma separator: - if cfg!(debug_assertions) { eprint!(", "); } + if cfg!(debug_assertions) { + write!(&mut err, ", ").unwrap(); + } { // Evaluate, tmp is value: let tmp = $val; // Print out $lab = tmp: if cfg!(debug_assertions) { - eprint!("{} = {:#?}", stringify!($lab), tmp); + write!(&mut err, "{} = {:#?}", stringify!($lab), tmp) + .unwrap(); } // Yield tmp: tmp @@ -429,7 +447,7 @@ macro_rules! dbg { ); // Newline: - if cfg!(debug_assertions) { eprintln!(""); } + if cfg!(debug_assertions) { writeln!(&mut err, "").unwrap(); } // Return the expression: ret @@ -438,6 +456,11 @@ macro_rules! dbg { } ``` +A notable difference to this exact implementation compared to the schematic +explanation and the example implementation that it locks `STDERR` once and uses +a buffered writer for efficiency and concistency when dealing with multiple +threads that write out to `STDERR` concurrently. + # Drawbacks [drawbacks]: #drawbacks From 193bf006811ece5749ba11919bd1f8b75d7d69f7 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 18:41:44 +0200 Subject: [PATCH 12/30] rfc, quick_debug_macro: readded column!() to currently unresolved + pro/cons for it --- text/0000-quick-debug-macro.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 1031a0e3168..1a0e7b4535c 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -490,7 +490,6 @@ Some questions regarding the format were: 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, were: @@ -505,6 +504,20 @@ They have all been answered in the affirmative. ## Currently unresolved +Some questions regarding the format are: + +3. Should the column number be included? + +It is more likely than not that no more than one `dbg!(...)` will occur. If it +does, it will most likely be when dealing with binary operators such as with: +`dbg!(x) + dbg!(y) + dbg!(z)`, or with several arguments to a function/method +call. However, since the macro prints out `stringify!(expr)`, which in the case +of the additions would result in: `x = , y = , z = `, the user +can clearly see which expression on the line that generated the value. The only +exception to this is if the same expression is used multiple times and crucically +has side effects altering the value between calls. This scenario is probably +very uncommon. + [`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 To be revisited once [`specialization`] From 4769aec8db289f0656cc684b944c2427cf1b4040 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 18:42:45 +0200 Subject: [PATCH 13/30] rfc, quick_debug_macro: moved macro name bikeshed to # rationale & alternatives --- text/0000-quick-debug-macro.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 1a0e7b4535c..3d0a0bc8d60 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -49,21 +49,6 @@ 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. -## Bikeshed: The name of the macro - -Several names has been proposed for the macro. Some of the candidates were: - -+ `debug!`, which was the original name. This was however already used by the -`log` crate. -+ `d!`, which was deemded to be too short to be informative and convey intent. -+ `dump!`, which was confused with stack traces. -+ `show!`, inspired by Haskell. `show` was deemed less obvious than `dbg!`. -+ `peek!`, which was also deemed less obvious. -+ `DEBUG!`, which was deemed too screamy. - -While it is unfortunate that `debug!` was unavailable, `dbg!` was deemed the -next best thing, which is why it was picked as the name of the macro. - # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -479,6 +464,21 @@ header via an env var strikes a good balance. The impact of not merging the RFC is that the papercut, if considered as such, remains. +## Bikeshed: The name of the macro + +Several names has been proposed for the macro. Some of the candidates were: + ++ `debug!`, which was the original name. This was however already used by the +`log` crate. ++ `d!`, which was deemded to be too short to be informative and convey intent. ++ `dump!`, which was confused with stack traces. ++ `show!`, inspired by Haskell. `show` was deemed less obvious than `dbg!`. ++ `peek!`, which was also deemed less obvious. ++ `DEBUG!`, which was deemed too screamy. + +While it is unfortunate that `debug!` was unavailable, `dbg!` was deemed the +next best thing, which is why it was picked as the name of the macro. + # Unresolved questions [unresolved]: #unresolved-questions From f4dfdd5de9af7d6f584612a72244417d8ef450dd Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 18:53:09 +0200 Subject: [PATCH 14/30] rfc, quick_debug_macro: added How do we teach this? section on pedagogy of teaching Rust with the macro --- text/0000-quick-debug-macro.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 3d0a0bc8d60..0a833ff185a 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -479,6 +479,17 @@ Several names has been proposed for the macro. Some of the candidates were: While it is unfortunate that `debug!` was unavailable, `dbg!` was deemed the next best thing, which is why it was picked as the name of the macro. +## How do we teach this? + +Part of the [motivation][[for-aspiring-rustaceans]] for this macro was to delay +the point at which aspiring rustaceans have to learn how formatting arguments +work in the language. For this to be effective, the macro should be taught prior +to teaching formatting arguments, but after teaching the user to write their +first "hello world" and other uses of `println!("")` which does +not involve formatting arguments, which should first be taught when formatting +is actually interesting, and not as a part of printing out the value of an +expression. + # Unresolved questions [unresolved]: #unresolved-questions From 34c1d05bd614800c807a078dc5b4fa18d68f8840 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 18:55:32 +0200 Subject: [PATCH 15/30] rfc, quick_debug_macro: note on qdbg! --- text/0000-quick-debug-macro.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 0a833ff185a..cbf1fde761a 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -475,6 +475,8 @@ Several names has been proposed for the macro. Some of the candidates were: + `show!`, inspired by Haskell. `show` was deemed less obvious than `dbg!`. + `peek!`, which was also deemed less obvious. + `DEBUG!`, which was deemed too screamy. ++ `qdbg!`, which was deemed to hurt searchability and learnability since it +isn't prefixed with `d`(ebug). While it is unfortunate that `debug!` was unavailable, `dbg!` was deemed the next best thing, which is why it was picked as the name of the macro. From be6b4d135f397cd348d137dd1f99351745c6118c Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 19:08:56 +0200 Subject: [PATCH 16/30] rfc, quick_debug_macro: trailing newline => unresolved questions. --- text/0000-quick-debug-macro.md | 44 +++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index cbf1fde761a..c8181fc8120 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -529,7 +529,49 @@ of the additions would result in: `x = , y = , z = `, the user can clearly see which expression on the line that generated the value. The only exception to this is if the same expression is used multiple times and crucically has side effects altering the value between calls. This scenario is probably -very uncommon. +very uncommon. However, the `column!()` isn't very visually disturbing since +it uses horizontal screen real-estate but not vertical real-estate, which +may still be a good reason to keep it. + +8. Should a trailing newline be added after each `dbg!(exprs...)`? The result +of answer in the affirmative would be to change the following example: + +``` +[DEBUGGING, src/main.rs:85:18]: +=> a = 1 +[DEBUGGING, src/main.rs:86:25]: +=> a = 1, b = 2 +[DEBUGGING, src/main.rs:87:30]: +=> a = 1, b = 2, a + b = 3 +``` + +into: + +``` +[DEBUGGING, src/main.rs:85:18]: +=> a = 1 + +[DEBUGGING, src/main.rs:86:25]: +=> a = 1, b = 2 + +[DEBUGGING, src/main.rs:87:30]: +=> a = 1, b = 2, a + b = 3 +``` + +This may, to many readers, look considerably more readable thanks to visual +association of a particular set of values with the `DEBUGGING` header and make +the users own `println!(..)` and `eprintln!(..)` calls stand out more due to the +absence of the header. + +A counter argument to this is that users with IDEs or vertically short terminals +may have as little as `25%` of vertical screen space allocated for the program's +output with the rest belonging to the actual code editor. To these users, lines +are too precious to waste in this manner since scrolling may require the use of +the mouse or switching of keyboard input focus. + +However, it is more unlikely that a user will see the information they are +looking for in a small window without scrolling. Here, searchability is aided by +grouping which is visually pleasing to process. [`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 From b081c820bd130242f44fdd231ab7ae43c4d760ff Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 21:53:57 +0200 Subject: [PATCH 17/30] rfc, quick_debug_macro: optimized macro for release builds wrt. compile time. --- text/0000-quick-debug-macro.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index c8181fc8120..241c7f13ffd 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -375,17 +375,14 @@ macro_rules! dbg { // 2. to ensure that the printed message is not interleaved, which // would disturb the readability of the output, by other messages to // STDERR. + #[cfg(debug_assertions)] use ::std::io::Write; #[cfg(debug_assertions)] let stderr = ::std::io::stderr(); #[cfg(debug_assertions)] let mut err = ::std::io::BufWriter::new(stderr.lock()); - // RELEASE: satisfy the type system with a Writer that's never used. - #[cfg(not(debug_assertions))] - let mut err = ::std::io::sink(); - - if cfg!(debug_assertions) { + #[cfg(debug_assertions)] { // Print out source location unless silenced: let p = option_env!("RUST_DBG_NO_LOCATION") .map_or(true, |s| s == "0"); @@ -405,7 +402,7 @@ macro_rules! dbg { // Evaluate, tmp is value: let tmp = $valf; // Print out $lab = tmp: - if cfg!(debug_assertions) { + #[cfg(debug_assertions)] { write!(&mut err, "{} = {:#?}", stringify!($labf), tmp) .unwrap(); } @@ -414,14 +411,14 @@ macro_rules! dbg { } $(, { // Comma separator: - if cfg!(debug_assertions) { + #[cfg(debug_assertions)] { write!(&mut err, ", ").unwrap(); } { // Evaluate, tmp is value: let tmp = $val; // Print out $lab = tmp: - if cfg!(debug_assertions) { + #[cfg(debug_assertions)] { write!(&mut err, "{} = {:#?}", stringify!($lab), tmp) .unwrap(); } @@ -432,7 +429,7 @@ macro_rules! dbg { ); // Newline: - if cfg!(debug_assertions) { writeln!(&mut err, "").unwrap(); } + #[cfg(debug_assertions)] { writeln!(&mut err, "").unwrap(); } // Return the expression: ret From 55b90ff2fe543039c77c5f8ce3e8c0d8097227c3 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 22:04:19 +0200 Subject: [PATCH 18/30] rfc, quick_debug_macro, ref-level-impl: notes on why macro is identity on release builds --- text/0000-quick-debug-macro.md | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 241c7f13ffd..9324676e9a2 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -443,6 +443,52 @@ explanation and the example implementation that it locks `STDERR` once and uses a buffered writer for efficiency and concistency when dealing with multiple threads that write out to `STDERR` concurrently. +On release builds, this macro reduces to: + +```rust +// For #[allow(unused_parens)]: +#![feature(stmt_expr_attributes)] + +#[macro_export] +macro_rules! dbg { + // ... + // With label: + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { + #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] + { + let ret = ( + { + let tmp = $valf; + tmp + } + $(, { + { + let tmp = $val; + tmp + } + } )* + ); + ret + } + }; +} +``` + +which further reduces to the following, which clearly shows that the invocation +is nothing more than the identity on the tuple passed: + +```rust +#[macro_export] +macro_rules! dbg { + // ... + + // With label: + ($($lab: expr => $val: expr)+) => {{ + ( $($val),* ) + }}; +} +``` + # Drawbacks [drawbacks]: #drawbacks From 34f8d6bd19427039053cf9ea8f2a1de4105e050e Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 22:06:37 +0200 Subject: [PATCH 19/30] rfc, quick_debug_macro: fixed mistake in prev commit + fixed link in HDWTT --- text/0000-quick-debug-macro.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 9324676e9a2..bb116094257 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -483,7 +483,7 @@ macro_rules! dbg { // ... // With label: - ($($lab: expr => $val: expr)+) => {{ + ($($lab: expr => $val: expr),+) => {{ ( $($val),* ) }}; } @@ -526,7 +526,7 @@ next best thing, which is why it was picked as the name of the macro. ## How do we teach this? -Part of the [motivation][[for-aspiring-rustaceans]] for this macro was to delay +Part of the [motivation][for-aspiring-rustaceans] for this macro was to delay the point at which aspiring rustaceans have to learn how formatting arguments work in the language. For this to be effective, the macro should be taught prior to teaching formatting arguments, but after teaching the user to write their From 08b5543b2c9f2cac80b95391d5efcbd4800d7120 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 22:26:06 +0200 Subject: [PATCH 20/30] rfc, quick_debug_macro: expanded on dbg! as identity in motivation --- text/0000-quick-debug-macro.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index bb116094257..6f141a95578 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -38,7 +38,26 @@ 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 without hurting performance while allowing the debugging of the program in debug -builds. +builds. To see why this is useful, consider starting off with: + +```rust +let c = fun(a) + fun(b); +let y = self.first().second(); +``` + +Now you want to inspect what `fun(a)` and `fun(b)` is, but not go through the +hassle of 1) saving `fun(a)` and `fun(b)` to a variable, 2) printing out the +variable, 3) then finally use it in the expression as `let c = fa + fb;`. +The same applies to inspecting the temporary state of `self.first()`. Instead of +this hassle, you can simply do: + +```rust +let c = dbg!(fun(a)) + dbg!(fun(b)); +let y = dbg!(self.first()).second(); +``` + +This modification is considerably smaller and disturbs flow while +developing code to a lesser degree. Additionally, by allowing the user to pass in multiple expressions and label them, the utility is further augmented. From 7307a6fdfc05f301cf0b741cd4b077845f9fb91c Mon Sep 17 00:00:00 2001 From: Mazdak Date: Wed, 18 Oct 2017 23:35:49 +0200 Subject: [PATCH 21/30] rfc, quick_debug_macro: enforce label is string literal + note on shorter format for literals --- text/0000-quick-debug-macro.md | 121 ++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 10 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 6f141a95578..cd565c05ad2 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -381,8 +381,74 @@ macro_rules! dbg { dbg!( $($lab => $val),+ ) }; // Without label, use source of $val: - ($($val: expr),+) => { - dbg!($($val => $val),+) + ($valf: expr $(, $val: expr)*) => { + #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] + { + // DEBUG: Lock STDERR in a buffered writer. + // Motivation: + // 1. to avoid needless re-locking of STDERR at every write(ln)!. + // 2. to ensure that the printed message is not interleaved, which + // would disturb the readability of the output, by other messages to + // STDERR. + #[cfg(debug_assertions)] + use ::std::io::Write; + #[cfg(debug_assertions)] + let stderr = ::std::io::stderr(); + #[cfg(debug_assertions)] + let mut err = ::std::io::BufWriter::new(stderr.lock()); + + #[cfg(debug_assertions)] { + // Print out source location unless silenced: + let p = option_env!("RUST_DBG_NO_LOCATION") + .map_or(true, |s| s == "0"); + if p { + writeln!(&mut err, "[DEBUGGING, {}:{}:{}]:", + file!(), line!(), column!()).unwrap(); + } + // Print out arrow (on a new line): + write!(&mut err, "=> ").unwrap(); + } + + // Foreach label and expression: + // 1. Evaluate each expression, + // 2. Print out $lab = value of expression + let ret = ( + { + // Evaluate, tmp is value: + let tmp = $valf; + // Print out $lab = tmp: + #[cfg(debug_assertions)] { + write!(&mut err, "{} = {:#?}", stringify!($valf), tmp) + .unwrap(); + } + // Yield tmp: + tmp + } + $(, { + // Comma separator: + #[cfg(debug_assertions)] { + write!(&mut err, ", ").unwrap(); + } + { + // Evaluate, tmp is value: + let tmp = $val; + // Print out $lab = tmp: + #[cfg(debug_assertions)] { + write!(&mut err, "{} = {:#?}", stringify!($val), tmp) + .unwrap(); + } + // Yield tmp: + tmp + } + } )* + ); + + // Newline: + #[cfg(debug_assertions)] { writeln!(&mut err, "").unwrap(); } + + // Return the expression: + ret + } }; // With label: ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { @@ -422,6 +488,11 @@ macro_rules! dbg { let tmp = $valf; // Print out $lab = tmp: #[cfg(debug_assertions)] { + // Enforce is_literal_string($labf): + let _ = concat!($labf, ""); + let _ : &'static str = $labf; + + // Print: write!(&mut err, "{} = {:#?}", stringify!($labf), tmp) .unwrap(); } @@ -438,6 +509,11 @@ macro_rules! dbg { let tmp = $val; // Print out $lab = tmp: #[cfg(debug_assertions)] { + // Enforce is_literal_string($lab): + let _ = concat!($labf, ""); + let _ : &'static str = $labf; + + // Print: write!(&mut err, "{} = {:#?}", stringify!($lab), tmp) .unwrap(); } @@ -471,6 +547,26 @@ On release builds, this macro reduces to: #[macro_export] macro_rules! dbg { // ... + + // Without label, use source of $val: + ($valf: expr $(, $val: expr)*) => { + #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] + { + let ret = ( + { + let tmp = $valf; + tmp + } + $(, { + { + let tmp = $val; + tmp + } + } )* + ); + ret + } + }; // With label: ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] @@ -500,11 +596,8 @@ is nothing more than the identity on the tuple passed: #[macro_export] macro_rules! dbg { // ... - - // With label: - ($($lab: expr => $val: expr),+) => {{ - ( $($val),* ) - }}; + ($( $val: expr),+) => {{ ( $($val),* ) }}; + ($($lab: expr => $val: expr),+) => {{ ( $($val),* ) }}; } ``` @@ -637,12 +730,20 @@ grouping which is visually pleasing to process. [`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 -To be revisited once [`specialization`] -has been stabilized: +To be revisited once [`specialization`] has been stabilized: [`debugit`]: https://docs.rs/debugit/0.1.2/debugit/ 7. Should expressions and values of non-`Debug` types be usable with this macro by using `std::intrinsics::type_name` for such types and the `Debug` impl for `T : Debug` types as done in version 0.1.2 of [`debugit`]? This depends on -specialization. \ No newline at end of file +specialization. + +[RFC 1576]: https://github.com/rust-lang/rfcs/pull/1576 + +To be revisited if and when [RFC 1576] has been stabilized: + +9. Should debugging literal expressions as in `dbg!(42);` print out `42 = 42` or +should it print out `5`? The left hand side of the equality adds no new +information wherefore it might be a redundant annoyance. On the other hand, +it may give a sense of symmetry with the non-literal forms such as `a = 42`. \ No newline at end of file From ee0cc86bf6d50e00ca6cf8241189d6bdf136f692 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Thu, 19 Oct 2017 23:10:07 +0200 Subject: [PATCH 22/30] rfc, quick_debug_macro: updated to reflect discussed changes --- text/0000-quick-debug-macro.md | 429 ++++++++++++++++++++++----------- 1 file changed, 282 insertions(+), 147 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index cd565c05ad2..8a13c3791c5 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -103,20 +103,19 @@ fn main() { The program will print the points to `STDERR` as: ``` -[DEBUGGING, src/main.rs:1:4]: +[DEBUGGING, src/main.rs:1] => Point{x: 1, y: 2,} = Point { x: 1, y: 2 } -[DEBUGGING, src/main.rs:7:4]: + +[DEBUGGING, src/main.rs:7] => 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. This is seen in the following example: @@ -131,13 +130,16 @@ fn main() { This prints the following to `STDERR`: ``` -[DEBUGGING, src/main.rs:1:12]: +[DEBUGGING, src/main.rs] => 1 + 2 = 3 -[DEBUGGING, src/main.rs:2:12]: + +[DEBUGGING, src/main.rs] => x + 1 = 4 -[DEBUGGING, src/main.rs:2:26]: + +[DEBUGGING, src/main.rs] => 3 = 3 -[DEBUGGING, src/main.rs:3:4]: + +[DEBUGGING, src/main.rs] => y = 7 ``` @@ -163,13 +165,16 @@ As seen in the example, the type of the expression `dbg!(expr)` is the type of The example above prints the following to `STDERR`: ``` -[DEBUGGING, src/main.rs:3:18]: +[DEBUGGING, src/main.rs:3] => a = 1 -[DEBUGGING, src/main.rs:4:25]: + +[DEBUGGING, src/main.rs:4] => a = 1, b = 2 -[DEBUGGING, src/main.rs:5:30]: + +[DEBUGGING, src/main.rs:5] => a = 1, b = 2, a + b = 3 -[DEBUGGING, src/main.rs:9:31]: + +[DEBUGGING, src/main.rs:9] => &p = Point { x: 4, y: 5 @@ -189,36 +194,58 @@ fn main() { dbg!("width" => w, "height" => h, "area" => w * h); let p = Point { x: 4, y: 5 }; - let q = Point { x: 2, y: 1 }; - dbg!("first point" => &p, "second point" => &p); + dbg!("first point" => &p, "same point" => &p); } ``` This allows the user to provide more descriptive names if necessary. With this example, the following is printed to `STDERR`: ``` -[DEBUGGING, src/main.rs:2:4]: +[DEBUGGING, src/main.rs:2] => "width" = 1, "height" = 2, "area" = 2 -[DEBUGGING, src/main.rs:7:4]: + +[DEBUGGING, src/main.rs:7]: => "first point" = Point { x: 4, y: 5 -}, "second point" = Point { - x: 2, - y: 1 +}, "same point" = Point { + x: 4, + y: 5 } ``` +It is important to note here that since the type `Point` is not `Copy`, it has +move semantics. Since `dbg!(p)` would involve moving `p`, using `dbg!(p, p);` +would involve moving the value twice, which Rust will not allow. Therefore, +a borrow to `p` is used in `dbg!("first point" => &p, "second point" => &p);`. + The ways of using the macro used in later (not the first) examples will mostly benefit existing Rust programmers. -### Omitting the source location +### Compact mode: Those developers who feel the source location header is overly verbose may -choose to opt-out by setting the environment variable `RUST_DBG_NO_LOCATION` to +choose to opt-out by setting the environment variable `RUST_DBG_COMPACT` to `"0"`. This is a one-time setup cost the developer has to make for all current and future Rust projects. +The effect of flipping this switch off is to print out the following instead +for the two last examples: + +``` +[src/main.rs:3] a = 1 +[src/main.rs:4] a = 1, b = 2 +[src/main.rs:5] a = 1, b = 2, a + b = 3 +[src/main.rs:9] &p = Point { x: 4, y: 5}, &q = Point { x: 2, y: 1 } +``` + +and: + +``` +[src/main.rs:2] "width" = 1, "height" = 2, "area" = 2 +[src/main.rs:7] "first point" = Point { x: 4, y: 5 }, "same point" = Point { x: 4, y: 5 } +``` + ## On release builds The same examples above will print nothing to `STDERR` and will instead simply @@ -227,6 +254,9 @@ evaluate the expressions. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation +**NOTE:** The exact output format is not meant to be stabilized even when/if the +macro is stabilized. + The macro is called `dbg` and accepts either a non-empty comma-separated or comma-terminated list of `expr`, or a non-empty list of `label => expr` which is also separated or terminated with commas. @@ -260,22 +290,25 @@ the value of `expr`. + Otherwise, `dbg!(expr1, expr2 [, expr3, .., exprN])`: The type is the type of the tuple `(expr1, expr2 [, expr3, .., exprN])` which is the value. -## Schematic/step-wise explanation +## Schematic/step-wise explanation (for debug builds) + +1. The standard error is locked and wrapped in a buffered writer called `err`. + +2. Assume `let p = option_env!("RUST_DBG_COMPACT").map_or(true, |s| s == "0");`. -1. Assume -`let p = option_env!("RUST_DBG_NO_LOCATION").map_or(true, |s| s == "0");`. -If `p` holds, the file name (given by `file!()`), line number (`line!()`) and -column (`column!()`) is included in the print out for increased utility when the -macro is used in non-trivial code. This is wrapped by `[DEBUGGING, ]:` -as in: +If `p` holds, the file name (given by `file!()`) and line number (`line!()`) is +included in the print out for increased utility when the macro is used in +non-trivial code. This is wrapped by `[DEBUGGING, ]\n=> ` as in: ```rust -eprintln!("[DEBUGGING, {}:{}:{}]:", file!(), line!(), column!()); +write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) ``` -If `p` does not hold, this step prints nothing. +If `p` does not hold, this instead prints: -2. An arrow is then printed on the next line: `eprint!("=> ");`. +```rust +write!(&mut err, "[{}:{}] ", file!(), line!()) +``` 3. + For `($($val: expr),+)` @@ -285,7 +318,9 @@ equality sign `=` while the result of `stringify!(expr)` is presented on the left hand side (LHS). This is done so that the developer easily can see the syntactic structure of the expression that evaluted to RHS. -In other words, the following: `eprint!("{} = {:#?}", stringify!($lab), tmp);`. +In other words, the following: +`write!(&mut err, "{} = {:#?}", stringify!($lab), tmp);` is done. +If `p` holds, `{:?}` is used as the format instead of `{:#?}`. 3. + For `($($lab: expr => $val: expr),+)`: @@ -293,12 +328,12 @@ For each `$lab => $val` (the label and expression), the following is printed, comma separated: The value of the expression is presented on RHS of an equality sign `=` while the label is presented on LHS. -In other words, the following: `eprint!("{} = {:#?}", stringify!($lab), tmp);`. +In other words, the following: +`write!(&mut err, "{} = {:#?}", stringify!($lab), tmp);` is done. +If `p` holds, `{:?}` is used as the format instead of `{:#?}`. +The label is also verified to be a string slice literal. -**NOTE:** The label is only guaranteed to work when it is a string slice literal. - -**NOTE:** The exact output format is not meant to be stabilized even when/if the -macro is stabilized. +4. Finally, a newline is printed, or two newlines in the case `p` holds. ## Example implementation @@ -306,66 +341,108 @@ The `dbg!` macro is semantically (with the notable detail that the helper macros and any non-`pub` `fn`s must be inlined in the actual implementation): ```rust -macro_rules! cfg_dbg { - ($($x:tt)+) => { - if cfg!(debug_assertions) { $($x)* } +// For #[allow(unused_parens)]: +#![feature(stmt_expr_attributes)] + +pub fn in_detailed_mode() -> bool { + option_env!("RUST_DBG_COMPACT").map_or(false, |s| s == "0") +} + +macro_rules! verify_str_lit { + ($($expr: expr),*) => { + $({ + let _ = concat!($expr, ""); + let _ : &'static str = $expr; + })* }; } -fn dbg_with_location() -> bool { - option_env!("RUST_DBG_NO_LOCATION").map_or_else(|| true, |s| s == "0") +macro_rules! w { + ($err: ident, $($t: tt)+) => { write!(&mut $err, $($t)+).unwrap(); } } macro_rules! dbg_header { - ($expr: expr) => {{ - cfg_dbg! { - if dbg_with_location() { - eprintln!("[DEBUGGING, {}:{}:{}]:", file!(), line!(), column!()); + ($err: ident) => { + #[cfg(debug_assertions)] { + if in_detailed_mode() { + w!($err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) + } else { + w!($err, "[{}:{}] ", file!(), line!()) } - eprint!("=> "); } - let ret = $expr; - cfg_dbg! { eprintln!(""); } - ret - }}; + }; } -macro_rules! dbg_comma { - ($valf: expr) => { $valf }; - ($valf: expr, $($val: expr),+) => { - ( $valf, $({ cfg_dbg! { eprint!(", "); } $val}),+ ) - } +macro_rules! dbg_footer { + ($err: ident) => { + #[cfg(debug_assertions)] { + if in_detailed_mode() { w!($err, "\n\n") } else { w!($err, "\n") } + } + }; } -macro_rules! dbg_term { - ($val: expr) => { dbg_term!($val => $val) }; - ($lab: expr => $val: expr) => {{ +macro_rules! dbg_expr { + ($err: ident, $lab: expr => $val: expr) => {{ let tmp = $val; - cfg_dbg! { eprint!("{} = {:#?}", stringify!($lab), tmp); } + #[cfg(debug_assertions)] { + let l = stringify!($lab); + if in_detailed_mode() { w!($err, "{} = {:#?}", l, tmp) } + else { w!($err, "{} = {:?}", l, tmp) } + } tmp - }}; + }} +} + +macro_rules! dbg_core { + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { + //#[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] + { + #[cfg(debug_assertions)] + use ::std::io::Write; + #[cfg(debug_assertions)] + let stderr = ::std::io::stderr(); + #[cfg(debug_assertions)] + let mut err = ::std::io::BufWriter::new(stderr.lock()); + + dbg_header!(err); + let ret = ( + dbg_expr!(err, $labf => $valf) + $(, { + #[cfg(debug_assertions)] w!(err, ", "); + dbg_expr!(err, $lab => $val) + } )* + ); + dbg_footer!(err); + ret + } + }; } #[macro_export] macro_rules! dbg { + // Handle trailing comma: ($($val: expr),+,) => { dbg!( $($val),+ ) }; ($($lab: expr => $val: expr),+,) => { dbg!( $($lab => $val),+ ) }; - ($($val: expr),+) => { - dbg_header!(dbg_comma!($(dbg_term!($val)),+)) - }; - ($($lab: expr => $val: expr),+) => { - dbg_header!(dbg_comma!($(dbg_term!($lab => $val)),+)) + // Without label, use source of $val: + ($valf: expr $(, $val: expr)*) => { + dbg_core!($valf => $valf $(, $val => $val)*) }; + // With label: + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => {{ + verify_str_lit!($labf, $($lab),*); + dbg_core!($labf => $valf $(, $lab => $val)*) + }}; } ``` ## Exact implementation -The exact implementation is given by: +The exact implementation, which is authoritative on the semantics of this RFC, +is given by: ```rust // For #[allow(unused_parens)]: @@ -397,16 +474,20 @@ macro_rules! dbg { #[cfg(debug_assertions)] let mut err = ::std::io::BufWriter::new(stderr.lock()); - #[cfg(debug_assertions)] { - // Print out source location unless silenced: - let p = option_env!("RUST_DBG_NO_LOCATION") + // Are we in not in compact mode (detailed)? + // If so: + // + {:?} is used instead of {:#?}, + // + Header is: [] + #[cfg(debug_assertions)] + let detailed = option_env!("RUST_DBG_COMPACT") .map_or(true, |s| s == "0"); - if p { - writeln!(&mut err, "[DEBUGGING, {}:{}:{}]:", - file!(), line!(), column!()).unwrap(); - } - // Print out arrow (on a new line): - write!(&mut err, "=> ").unwrap(); + + #[cfg(debug_assertions)] { + (if detailed { + write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) + } else { + write!(&mut err, "[{}:{}] ", file!(), line!()) + }).unwrap() } // Foreach label and expression: @@ -418,8 +499,12 @@ macro_rules! dbg { let tmp = $valf; // Print out $lab = tmp: #[cfg(debug_assertions)] { - write!(&mut err, "{} = {:#?}", stringify!($valf), tmp) - .unwrap(); + let l = stringify!($valf); + (if detailed { + write!(&mut err, "{} = {:#?}", l, tmp) + } else { + write!(&mut err, "{} = {:?}", l, tmp) + }).unwrap() } // Yield tmp: tmp @@ -434,8 +519,12 @@ macro_rules! dbg { let tmp = $val; // Print out $lab = tmp: #[cfg(debug_assertions)] { - write!(&mut err, "{} = {:#?}", stringify!($val), tmp) - .unwrap(); + let l = stringify!($val); + (if detailed { + write!(&mut err, "{} = {:#?}", l, tmp) + } else { + write!(&mut err, "{} = {:?}", l, tmp) + }).unwrap() } // Yield tmp: tmp @@ -444,7 +533,13 @@ macro_rules! dbg { ); // Newline: - #[cfg(debug_assertions)] { writeln!(&mut err, "").unwrap(); } + #[cfg(debug_assertions)] { + (if detailed { + writeln!(&mut err, "\n") + } else { + writeln!(&mut err, "") + }).unwrap() + } // Return the expression: ret @@ -467,16 +562,20 @@ macro_rules! dbg { #[cfg(debug_assertions)] let mut err = ::std::io::BufWriter::new(stderr.lock()); - #[cfg(debug_assertions)] { - // Print out source location unless silenced: - let p = option_env!("RUST_DBG_NO_LOCATION") + // Are we in not in compact mode (detailed)? + // If so: + // + {:?} is used instead of {:#?}, + // + Header is: [] + #[cfg(debug_assertions)] + let detailed = option_env!("RUST_DBG_COMPACT") .map_or(true, |s| s == "0"); - if p { - writeln!(&mut err, "[DEBUGGING, {}:{}:{}]:", - file!(), line!(), column!()).unwrap(); - } - // Print out arrow (on a new line): - write!(&mut err, "=> ").unwrap(); + + #[cfg(debug_assertions)] { + (if detailed { + write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) + } else { + write!(&mut err, "[{}:{}] ", file!(), line!()) + }).unwrap() } // Foreach label and expression: @@ -493,8 +592,12 @@ macro_rules! dbg { let _ : &'static str = $labf; // Print: - write!(&mut err, "{} = {:#?}", stringify!($labf), tmp) - .unwrap(); + let l = stringify!($labf); + (if detailed { + write!(&mut err, "{} = {:#?}", l, tmp) + } else { + write!(&mut err, "{} = {:?}", l, tmp) + }).unwrap() } // Yield tmp: tmp @@ -510,12 +613,16 @@ macro_rules! dbg { // Print out $lab = tmp: #[cfg(debug_assertions)] { // Enforce is_literal_string($lab): - let _ = concat!($labf, ""); - let _ : &'static str = $labf; + let _ = concat!($lab, ""); + let _ : &'static str = $lab; // Print: - write!(&mut err, "{} = {:#?}", stringify!($lab), tmp) - .unwrap(); + let l = stringify!($lab); + (if detailed { + write!(&mut err, "{} = {:#?}", l, tmp) + } else { + write!(&mut err, "{} = {:?}", l, tmp) + }).unwrap() } // Yield tmp: tmp @@ -524,7 +631,13 @@ macro_rules! dbg { ); // Newline: - #[cfg(debug_assertions)] { writeln!(&mut err, "").unwrap(); } + #[cfg(debug_assertions)] { + (if detailed { + writeln!(&mut err, "\n") + } else { + writeln!(&mut err, "") + }).unwrap() + } // Return the expression: ret @@ -533,11 +646,6 @@ macro_rules! dbg { } ``` -A notable difference to this exact implementation compared to the schematic -explanation and the example implementation that it locks `STDERR` once and uses -a buffered writer for efficiency and concistency when dealing with multiple -threads that write out to `STDERR` concurrently. - On release builds, this macro reduces to: ```rust @@ -647,32 +755,21 @@ not involve formatting arguments, which should first be taught when formatting is actually interesting, and not as a part of printing out the value of an expression. -# Unresolved questions -[unresolved]: #unresolved-questions - -The format used by the macro should be resolved prior to merging. - -## Formerly unresolved +## Formerly unresolved questions +[formerly unresolved]: #formerly-unresolved-questions Some questions regarding the format were: 1. Should the `file!()` be included? -2. Should the line number be included? -4. Should the `stringify!($val)` be included? - -Other questions, which should also be resolved prior to merging, were: - -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. -They have all been answered in the affirmative. +**Yes**, since it would be otherwise difficult to tell where the output is coming +from in a larger project with multiple files. It is not very useful on +the [playground](https://play.rust-lang.org), but that exception is acceptable. -## Currently unresolved +2. Should the line number be included? -Some questions regarding the format are: +**Yes**, for a large file, it would also be difficult to locate the source of the +output otherwise. 3. Should the column number be included? @@ -684,39 +781,64 @@ of the additions would result in: `x = , y = , z = `, the user can clearly see which expression on the line that generated the value. The only exception to this is if the same expression is used multiple times and crucically has side effects altering the value between calls. This scenario is probably -very uncommon. However, the `column!()` isn't very visually disturbing since -it uses horizontal screen real-estate but not vertical real-estate, which -may still be a good reason to keep it. +very uncommon. Furthermore, even in this case, one can distinguish between the +calls since one is first and the second comes next, visually. + +However, the `column!()` isn't very visually disturbing since it uses horizontal +screen real-estate but not vertical real-estate, which may still be a good reason +to keep it. Nonetheless, this argument is not sufficient to keep `column!()`, +wherefore **this RFC will not include it**. + +4. Should the `stringify!($val)` be included? **[answer: yes]** + +Other, now resolved, questions, were: + +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? -8. Should a trailing newline be added after each `dbg!(exprs...)`? The result -of answer in the affirmative would be to change the following example: +**Yes**, the pass-through mechanism allows the macro to be less intrusive as +discussed in the [motivation]. + +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. + +**Yes**, since some users who develop programs, and not libraries, can leave +such `dbg!(..)` invocations in and push it to source control since it won't +affect debug builds of the program. + +8. Should a trailing newline be added after each `dbg!(exprs...)`? + +**Short answer: Yes.** + +The result of answer in the negative would use the following format: ``` -[DEBUGGING, src/main.rs:85:18]: +[DEBUGGING, src/main.rs:85] => a = 1 -[DEBUGGING, src/main.rs:86:25]: +[DEBUGGING, src/main.rs:86] => a = 1, b = 2 -[DEBUGGING, src/main.rs:87:30]: +[DEBUGGING, src/main.rs:87] => a = 1, b = 2, a + b = 3 ``` -into: +instead of: ``` -[DEBUGGING, src/main.rs:85:18]: +[DEBUGGING, src/main.rs:85] => a = 1 -[DEBUGGING, src/main.rs:86:25]: +[DEBUGGING, src/main.rs:86] => a = 1, b = 2 -[DEBUGGING, src/main.rs:87:30]: +[DEBUGGING, src/main.rs:87] => a = 1, b = 2, a + b = 3 ``` -This may, to many readers, look considerably more readable thanks to visual -association of a particular set of values with the `DEBUGGING` header and make -the users own `println!(..)` and `eprintln!(..)` calls stand out more due to the -absence of the header. +The latter format, to many readers, look considerably more readable thanks to +visual association of a particular set of values with the `DEBUGGING` header and +make the users own `println!(..)` and `eprintln!(..)` calls stand out more due +to the absence of the header. A counter argument to this is that users with IDEs or vertically short terminals may have as little as `25%` of vertical screen space allocated for the program's @@ -728,6 +850,28 @@ However, it is more unlikely that a user will see the information they are looking for in a small window without scrolling. Here, searchability is aided by grouping which is visually pleasing to process. +This was resolved by having the env var `RUST_DBG_COMPACT = 0` format the above +example as: + +``` +[src/main.rs:85] a = 1 +[src/main.rs:86] a = 1, b = 2 +[src/main.rs:87] a = 1, b = 2, a + b = 3 +``` + +9. Should debugging literal expressions as in `dbg!(42);` print out `42 = 42` or +should it print out `5`? + +**No**. The left hand side of the equality adds no new information wherefore it +might be a redundant annoyance. On the other hand, it may give a sense of +symmetry with the non-literal forms such as `a = 42`. Keeping `5 = 5` is also +more consistent, wherefore that format will be used. + +# Unresolved questions +[unresolved]: #unresolved-questions + +The format used by the macro should be resolved prior to merging. + [`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 To be revisited once [`specialization`] has been stabilized: @@ -737,13 +881,4 @@ To be revisited once [`specialization`] has been stabilized: 7. Should expressions and values of non-`Debug` types be usable with this macro by using `std::intrinsics::type_name` for such types and the `Debug` impl for `T : Debug` types as done in version 0.1.2 of [`debugit`]? This depends on -specialization. - -[RFC 1576]: https://github.com/rust-lang/rfcs/pull/1576 - -To be revisited if and when [RFC 1576] has been stabilized: - -9. Should debugging literal expressions as in `dbg!(42);` print out `42 = 42` or -should it print out `5`? The left hand side of the equality adds no new -information wherefore it might be a redundant annoyance. On the other hand, -it may give a sense of symmetry with the non-literal forms such as `a = 42`. \ No newline at end of file +specialization. \ No newline at end of file From 8fc21ec65123e2e65c632c51026f53ded2b7cc31 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 20 Oct 2017 00:07:30 +0200 Subject: [PATCH 23/30] rfc, quick_debug_macro: improved guide-level-explanation with factorial example --- text/0000-quick-debug-macro.md | 233 ++++++++++++++++++++++++++++++--- 1 file changed, 214 insertions(+), 19 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 8a13c3791c5..30cd273458c 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -73,6 +73,8 @@ logged. It is therefore not suitable for introducing newcommers to the language. ## On debug builds +[on-debug-builds]: #on-debug-builds + First, some preliminaries: ```rust @@ -87,15 +89,9 @@ 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!(Point { x: 1, y: 2 }); + + let p = Point { x: 4, y: 5 }; dbg!(p); } ``` @@ -109,7 +105,7 @@ The program will print the points to `STDERR` as: y: 2 } -[DEBUGGING, src/main.rs:7] +[DEBUGGING, src/main.rs:4] => p = Point { x: 4, y: 5 @@ -130,16 +126,16 @@ fn main() { This prints the following to `STDERR`: ``` -[DEBUGGING, src/main.rs] +[DEBUGGING, src/main.rs:1] => 1 + 2 = 3 -[DEBUGGING, src/main.rs] +[DEBUGGING, src/main.rs:2] => x + 1 = 4 -[DEBUGGING, src/main.rs] +[DEBUGGING, src/main.rs:2] => 3 = 3 -[DEBUGGING, src/main.rs] +[DEBUGGING, src/main.rs:3] => y = 7 ``` @@ -201,10 +197,10 @@ fn main() { This allows the user to provide more descriptive names if necessary. With this example, the following is printed to `STDERR`: ``` -[DEBUGGING, src/main.rs:2] +[DEBUGGING, src/main.rs:3] => "width" = 1, "height" = 2, "area" = 2 -[DEBUGGING, src/main.rs:7]: +[DEBUGGING, src/main.rs:6]: => "first point" = Point { x: 4, y: 5 @@ -230,7 +226,7 @@ choose to opt-out by setting the environment variable `RUST_DBG_COMPACT` to and future Rust projects. The effect of flipping this switch off is to print out the following instead -for the two last examples: +for the two last examples in [on-debug-builds]: ``` [src/main.rs:3] a = 1 @@ -242,8 +238,8 @@ for the two last examples: and: ``` -[src/main.rs:2] "width" = 1, "height" = 2, "area" = 2 -[src/main.rs:7] "first point" = Point { x: 4, y: 5 }, "same point" = Point { x: 4, y: 5 } +[src/main.rs:3] "width" = 1, "height" = 2, "area" = 2 +[src/main.rs:6] "first point" = Point { x: 4, y: 5 }, "same point" = Point { x: 4, y: 5 } ``` ## On release builds @@ -251,6 +247,205 @@ and: The same examples above will print nothing to `STDERR` and will instead simply evaluate the expressions. +## An example from the real world + +You have been given a task to implement `n!`, the factorial function - a common +task for those learning programming, which you have decided to implement using a +simple recursive solution looking like this: + +```rust +fn factorial(n: u32) -> u32 { + if n <= 1 { + 1 + } else { + n * factorial(n - 1) + } +} + +fn main() { + factorial(4); +} +``` + +Now, you, as a learner, want to see how the recursion expands, and use the +`dbg!` macro to your aid: + +```rust +fn factorial(n: u32) -> u32 { + if dbg!(n <= 1) { + dbg!(1) + } else { + dbg!(n * factorial(n - 1)) + } +} + +fn main() { + dbg!(factorial(4)); +} +``` + +You run the program, and get back a print out which clearly shows the function +recursively descending into a stack, before ascending and building the final +value, and then it shows the final answer again. + +``` +[DEBUGGING, src/main.rs:1] +=> n <= 1 = false + +[DEBUGGING, src/main.rs:1] +=> n <= 1 = false + +[DEBUGGING, src/main.rs:1] +=> n <= 1 = false + +[DEBUGGING, src/main.rs:1] +=> n <= 1 = true + +[DEBUGGING, src/main.rs:2] +=> 1 = 1 + +[DEBUGGING, src/main.rs:4] +=> n * factorial(n - 1) = 2 + +[DEBUGGING, src/main.rs:4] +=> n * factorial(n - 1) = 6 + +[DEBUGGING, src/main.rs:4] +=> n * factorial(n - 1) = 24 + +[DEBUGGING, src/main.rs:9] +=> factorial(4) = 24 +``` + +or prints, with `RUST_DBG_COMPACT = 1`: + +``` +[src/main.rs:1] n <= 1 = false +[src/main.rs:1] n <= 1 = false +[src/main.rs:1] n <= 1 = false +[src/main.rs:1] n <= 1 = true +[src/main.rs:2] 1 = 1 +[src/main.rs:4] n * factorial(n - 1) = 2 +[src/main.rs:4] n * factorial(n - 1) = 6 +[src/main.rs:4] n * factorial(n - 1) = 24 +[src/main.rs:9] factorial(4) = 24 +``` + +But you like labels, so you use them instead: + +```rust +fn factorial(n: u32) -> u32 { + if dbg!("are we at the base case?" => n <= 1) { + dbg!("base value" => 1) + } else { + dbg!("ascending with n * factorial(n - 1)" => n * factorial(n - 1)) + } +} +``` + +which prints: + +``` +[DEBUGGING, src/main.rs:1] +=> "are we at the base case?" = false + +[DEBUGGING, src/main.rs:1] +=> "are we at the base case?" = false + +[DEBUGGING, src/main.rs:1] +=> "are we at the base case?" = false + +[DEBUGGING, src/main.rs:1] +=> "are we at the base case?" = true + +[DEBUGGING, src/main.rs:2] +=> "base value" = 1 + +[DEBUGGING, src/main.rs:4] +=> "ascending with n * factorial(n - 1)" = 2 + +[DEBUGGING, src/main.rs:4] +=> "ascending with n * factorial(n - 1)" = 6 + +[DEBUGGING, src/main.rs:4] +=> "ascending with n * factorial(n - 1)" = 24 + +[DEBUGGING, src/main.rs:9] +=> factorial(4) = 24 +``` + +or prints, with `RUST_DBG_COMPACT = 1`: + +``` +[src/main.rs:1] "are we at the base case?" = false +[src/main.rs:1] "are we at the base case?" = false +[src/main.rs:1] "are we at the base case?" = false +[src/main.rs:1] "are we at the base case?" = true +[src/main.rs:2] "base value" = 1 +[src/main.rs:4] "ascending with n * factorial(n - 1)" = 2 +[src/main.rs:4] "ascending with n * factorial(n - 1)" = 6 +[src/main.rs:4] "ascending with n * factorial(n - 1)" = 24 +[src/main.rs:9] factorial(4) = 24 +``` + +Finally, you'd also like to see the value of `n` at each recursion step. Using +the multiple-arguments feature, you write, with very little effort, and run: + +```rust +fn factorial(n: u32) -> u32 { + if dbg!(n, (n <= 1)).1 { + dbg!(n, 1).1 + } else { + dbg!(n, n * factorial(n - 1)).1 + } +} +``` + +which outputs: + +``` +[DEBUGGING, src/main.rs:1] +=> n = 4, (n <= 1) = false + +[DEBUGGING, src/main.rs:1] +=> n = 3, (n <= 1) = false + +[DEBUGGING, src/main.rs:1] +=> n = 2, (n <= 1) = false + +[DEBUGGING, src/main.rs:1] +=> n = 1, (n <= 1) = true + +[DEBUGGING, src/main.rs:2] +=> n = 1, 1 = 1 + +[DEBUGGING, src/main.rs:4] +=> n = 2, n * factorial(n - 1) = 2 + +[DEBUGGING, src/main.rs:4] +=> n = 3, n * factorial(n - 1) = 6 + +[DEBUGGING, src/main.rs:4] +=> n = 4, n * factorial(n - 1) = 24 + +[DEBUGGING, src/main.rs:9] +=> factorial(4) = 24 +``` + +or prints, with `RUST_DBG_COMPACT = 1`: + +``` +[src/main.rs:1] n = 4, (n <= 1) = false +[src/main.rs:1] n = 3, (n <= 1) = false +[src/main.rs:1] n = 2, (n <= 1) = false +[src/main.rs:1] n = 1, (n <= 1) = true +[src/main.rs:2] n = 1, 1 = 1 +[src/main.rs:4] n = 2, n * factorial(n - 1) = 2 +[src/main.rs:4] n = 3, n * factorial(n - 1) = 6 +[src/main.rs:4] n = 4, n * factorial(n - 1) = 24 +[src/main.rs:9] factorial(4) = 24 +``` + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From f98c6ad2256db6c9b4c761e2c570a6c9e0459041 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Tue, 24 Oct 2017 01:02:52 +0200 Subject: [PATCH 24/30] rfc, quick_debug_macro: more Q & A + minor formatting fixes --- text/0000-quick-debug-macro.md | 48 ++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 30cd273458c..794a1bc4fcb 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -654,6 +654,7 @@ macro_rules! dbg { }; // Without label, use source of $val: ($valf: expr $(, $val: expr)*) => { + #[allow(unused_must_use)] // for clarification on: dbg!(expr); #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { // DEBUG: Lock STDERR in a buffered writer. @@ -742,6 +743,7 @@ macro_rules! dbg { }; // With label: ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { + #[allow(unused_must_use)] // for clarification on: dbg!(expr); #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { // DEBUG: Lock STDERR in a buffered writer. @@ -853,6 +855,7 @@ macro_rules! dbg { // Without label, use source of $val: ($valf: expr $(, $val: expr)*) => { + #[allow(unused_must_use)] // for clarification on: dbg!(expr); #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { let ret = ( @@ -872,6 +875,7 @@ macro_rules! dbg { }; // With label: ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { + #[allow(unused_must_use)] // for clarification on: dbg!(expr); #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { let ret = ( @@ -919,6 +923,9 @@ on what is valued. A more terse format could be used if `stringify!` or argues it should. The RFC argues that the possibility of opting out to this header via an env var strikes a good balance. +For more alternatives, questions and how they were resolved, +see [formerly unresolved] for a more detailed Q & A. + The impact of not merging the RFC is that the papercut, if considered as such, remains. @@ -984,9 +991,11 @@ screen real-estate but not vertical real-estate, which may still be a good reaso to keep it. Nonetheless, this argument is not sufficient to keep `column!()`, wherefore **this RFC will not include it**. -4. Should the `stringify!($val)` be included? **[answer: yes]** +4. Should the `stringify!($val)` be included? + +**Yes**, it helps the user see the source of the value printed. -Other, now resolved, questions, were: +Other, now resolved, questions were: 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 @@ -1062,6 +1071,41 @@ might be a redundant annoyance. On the other hand, it may give a sense of symmetry with the non-literal forms such as `a = 42`. Keeping `5 = 5` is also more consistent, wherefore that format will be used. +10. Should `dbg!(expr);` generate an "unused" warning? + +**No**. In the case of: + +```rust +fn main() { + let a = 42; + dbg!(a); +} +``` + +the macro is used in "print" mode instead of "passhrough inspector" mode. +Both are expected and supported ways of using this macro wherefore no warning +should be raised. + +11. Should `STDOUT` be used over `STDERR` as the output stream? + +**Short answer: No.** + +The messages printed using `dbg!(..)` are not usually errors, which is +one reason to use `STDOUT` instead. However, `STDERR` is often used as a second +channel for extra messages. This use of `STDERR` often occurs when `STDOUT` +carries some data which you can't mix with random messages. + +If we consider a program such as `ripgrep`, where should hypothetical uses of +`dbg!(..)` print to in the case of `rg some_word < input_file > matching_lines`? +Should they end up on the terminal or in the file `matching_lines`? Clearly +the former is correct in this case. + +One could say that this design is a lousy choice by the programmer and that +debug messages should be logged to a file, but this macro must cater to "lousy" +programmers who just want to debug quickly. + +For these reasons, `STDERR` should be used. + # Unresolved questions [unresolved]: #unresolved-questions From 4c83cb5f92a4192120bd77ba0acdf231af9ac3a2 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Tue, 24 Oct 2017 06:11:20 +0200 Subject: [PATCH 25/30] rfc, quick_debug_macro: added notes on panic!()s --- text/0000-quick-debug-macro.md | 185 +++++++++++++++++++++++---------- 1 file changed, 132 insertions(+), 53 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 794a1bc4fcb..a44158cea07 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -242,6 +242,36 @@ and: [src/main.rs:6] "first point" = Point { x: 4, y: 5 }, "same point" = Point { x: 4, y: 5 } ``` +## Dealing with panics +[dealing with panics]: #dealing-with-panics + +In the following example we have a panic in the second argument: + +```rust +fn main() { + let (a, b) = (1, 2); + dbg!(a, panic!(), b); +} +``` + +running this program will print the following to `STDERR`: + +``` +[DEBUGGING, src/main.rs:2] +=> a = 1, panic!() = +``` + +and to `STDOUT`: + +``` +thread 'main' panicked at 'explicit panic', src/main.rs:2:12 +``` + +As can be seen from output, nothing is printed on RHS of `panic!()`. Why? +Because there's no value to present on RHS. Since a panic may cause necessary +side effects for subsequent arguments in `dbg!(..)` to not happen, the macro is +fail-fast on any panic in order to avoid cascading panics and unsafety. + ## On release builds The same examples above will print nothing to `STDERR` and will instead simply @@ -530,6 +560,8 @@ The label is also verified to be a string slice literal. 4. Finally, a newline is printed, or two newlines in the case `p` holds. +In both 3. and 4. if RHS should panic, then LHS = shall at least be printed. + ## Example implementation The `dbg!` macro is semantically (with the notable detail that the helper macros @@ -578,19 +610,20 @@ macro_rules! dbg_footer { macro_rules! dbg_expr { ($err: ident, $lab: expr => $val: expr) => {{ - let tmp = $val; + #[cfg(debug_assertions)] { w!($err, "{} = ", $lab) } + let _tmp = $val; #[cfg(debug_assertions)] { - let l = stringify!($lab); - if in_detailed_mode() { w!($err, "{} = {:#?}", l, tmp) } - else { w!($err, "{} = {:?}", l, tmp) } + if in_detailed_mode() { w!($err, "{:#?}", _tmp) } + else { w!($err, "{:?}" , _tmp) } } - tmp + _tmp }} } macro_rules! dbg_core { ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { - //#[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] + #[allow(unreachable_code)] // for panics. + #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { #[cfg(debug_assertions)] use ::std::io::Write; @@ -654,6 +687,7 @@ macro_rules! dbg { }; // Without label, use source of $val: ($valf: expr $(, $val: expr)*) => { + #[allow(unreachable_code)] // for panics. #[allow(unused_must_use)] // for clarification on: dbg!(expr); #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { @@ -689,21 +723,22 @@ macro_rules! dbg { // Foreach label and expression: // 1. Evaluate each expression, // 2. Print out $lab = value of expression - let ret = ( + let _ret = ( { + // Print out $lab = : + #[cfg(debug_assertions)] { + write!(&mut err, "{} = ", stringify!($valf)).unwrap() + } // Evaluate, tmp is value: - let tmp = $valf; - // Print out $lab = tmp: + let _tmp = $valf; // Won't get further if $val panics. + // Print out tmp: #[cfg(debug_assertions)] { - let l = stringify!($valf); - (if detailed { - write!(&mut err, "{} = {:#?}", l, tmp) - } else { - write!(&mut err, "{} = {:?}", l, tmp) - }).unwrap() + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) } + ).unwrap() } // Yield tmp: - tmp + _tmp } $(, { // Comma separator: @@ -711,19 +746,20 @@ macro_rules! dbg { write!(&mut err, ", ").unwrap(); } { + // Print out $lab = : + #[cfg(debug_assertions)] { + write!(&mut err, "{} = ", stringify!($val)).unwrap() + } // Evaluate, tmp is value: - let tmp = $val; - // Print out $lab = tmp: + let _tmp = $val; // Won't get further if $val panics. + // Print out tmp: #[cfg(debug_assertions)] { - let l = stringify!($val); - (if detailed { - write!(&mut err, "{} = {:#?}", l, tmp) - } else { - write!(&mut err, "{} = {:?}", l, tmp) - }).unwrap() + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) } + ).unwrap() } // Yield tmp: - tmp + _tmp } } )* ); @@ -738,11 +774,12 @@ macro_rules! dbg { } // Return the expression: - ret + _ret } }; // With label: ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { + #[allow(unreachable_code)] // for panics. #[allow(unused_must_use)] // for clarification on: dbg!(expr); #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] { @@ -778,26 +815,25 @@ macro_rules! dbg { // Foreach label and expression: // 1. Evaluate each expression, // 2. Print out $lab = value of expression - let ret = ( + let _ret = ( { - // Evaluate, tmp is value: - let tmp = $valf; - // Print out $lab = tmp: + // Print out $lab = : #[cfg(debug_assertions)] { - // Enforce is_literal_string($labf): + // Enforce is_literal_string($lab): let _ = concat!($labf, ""); let _ : &'static str = $labf; - - // Print: - let l = stringify!($labf); - (if detailed { - write!(&mut err, "{} = {:#?}", l, tmp) - } else { - write!(&mut err, "{} = {:?}", l, tmp) - }).unwrap() + write!(&mut err, "{} = ", stringify!($labf)).unwrap() + } + // Evaluate, tmp is value: + let _tmp = $valf; // Won't get further if $valf panics. + // Print out tmp: + #[cfg(debug_assertions)] { + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) } + ).unwrap() } // Yield tmp: - tmp + _tmp } $(, { // Comma separator: @@ -805,24 +841,23 @@ macro_rules! dbg { write!(&mut err, ", ").unwrap(); } { - // Evaluate, tmp is value: - let tmp = $val; - // Print out $lab = tmp: + // Print out $lab = : #[cfg(debug_assertions)] { // Enforce is_literal_string($lab): let _ = concat!($lab, ""); let _ : &'static str = $lab; - - // Print: - let l = stringify!($lab); - (if detailed { - write!(&mut err, "{} = {:#?}", l, tmp) - } else { - write!(&mut err, "{} = {:?}", l, tmp) - }).unwrap() + write!(&mut err, "{} = ", stringify!($lab)).unwrap() + } + // Evaluate, tmp is value: + let _tmp = $val; // Won't get further if $valf panics. + // Print out tmp: + #[cfg(debug_assertions)] { + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) } + ).unwrap() } // Yield tmp: - tmp + _tmp } } )* ); @@ -837,7 +872,7 @@ macro_rules! dbg { } // Return the expression: - ret + _ret } }; } @@ -1106,6 +1141,50 @@ programmers who just want to debug quickly. For these reasons, `STDERR` should be used. +12. Should `std::panic::catch_unwind` be used to handle panics? + +**Short answer: No.** + +If `expr` in `dbg!("label" => expr)` panics, should something be printed on the RHS of `"label" => ` as in: `"label" => ` ? If so, should all panics be +caught such that: + +```rust +fn main() { + let (a, b) = (1, 2); + dbg!(a, panic!(), b); +} +``` + +prints (using `RUST_DBG_COMPACT = 1`) to `STDERR`: + +```rust +[src/main.rs:2] a = 1, panic!() = , b = 2 +``` + +and to `STDOUT`: + +``` +thread 'main' panicked at 'explicit panic', src/main.rs:2:12 +``` + +and a single panic "re-thrown" after everything has been printed? + +This is a bad idea for two reasons: + +1. If `foo()` panics in `(foo(), bar())`, then `bar()` is not evaluated. The +user should be able to expect similar semantics from `dbg!(foo(), bar())` to +`(foo(), bar())`. + +2. Given `(foo(), bar())`, a panic in `foo()` entails that the postconditions of +`foo()` aren't guaranteed. If `bar()` relies on these postconditions of `foo()` +in its preconditions, then since the postconditions do not always hold, `bar()` +must not be evaluated. + +Now that the second question has been resolved in the negative, we can resolve +the first one. Since `"label" => ` combined with a message in `STDOUT` as seen +in [dealing with panics] is sufficiently clear, the overhead of `catch_unwind` +is for very little gain, wherefore this question is too answered in the negative. + # Unresolved questions [unresolved]: #unresolved-questions From 708504ea69eb5fbf171f8c9f1fe7ec7ade729ecc Mon Sep 17 00:00:00 2001 From: Mazdak Date: Thu, 26 Oct 2017 11:30:22 +0200 Subject: [PATCH 26/30] rfc, quick_debug_macro: improved formatting + notes on specialization --- text/0000-quick-debug-macro.md | 199 +++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 47 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index a44158cea07..83505a0789b 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -277,6 +277,43 @@ fail-fast on any panic in order to avoid cascading panics and unsafety. The same examples above will print nothing to `STDERR` and will instead simply evaluate the expressions. +## Types which are not `Debug` + +**This feature will be available once [`specialization`] has been stabilized +and not before.** + +If you are writing generic code and want to debug the value of some expression +`expr: T` where `T: Debug` might hold, but you don't want to add this to the +bound, you may simply use `dbg!(expr)`. This may be useful when you are deep +within some generic algorithm and the hassle of moving up the call stack is +in the way of productivity. Instead, if `T: Debug` holds implicitly, then the +debug impl will be used, otherwise we try to give as much helpful information +as we can. + +This is solved via [`specialization`]. The expression is first wrapped in a +struct which has a `Debug` impl for all types which gives information about the +type of the expression. For types which are `Debug`, the `Debug` impl of the +struct is specialized to use the `Debug` implementation of the wrapped type. + +With the following example: + +```rust +fn main() { + struct X(usize); + let a = X(1); + dbg!(&a); +} +``` + +the following is printed to `STDERR`: + +``` +[DEBUGGING, src/main.rs:3] +=> &a = [ of type &main::X is !Debug] +``` + +This tells you the typeof `&a`, and that it is not `Debug`. + ## An example from the real world You have been given a task to implement `n!`, the factorial function - a common @@ -943,6 +980,42 @@ macro_rules! dbg { } ``` +## Specialization and non-`Debug` types. + +**This feature will be available once [`specialization`] has been stabilized +and not before.** Once that happens, the feature will simply be added to the +macro without going through another RFC process. + +The following is added inside the macro: + +```rust +// All of this is internal to the macro and not exported: + +struct WrapDebug(T); + +use std::fmt::{Debug, Formatter, Result}; + +impl Debug for WrapDebug { + default fn fmt(&self, f: &mut Formatter) -> Result { + use std::intrinsics::type_name; + write!(f, "[ of type {} is !Debug]", + unsafe { type_name::() }) + } +} + +impl Debug for WrapDebug { + fn fmt(&self, f: &mut Formatter) -> Result { self.0.fmt(f) } +} +``` + +This mechanism is inspired by version 0.1.2 of [`debugit`]. + +Changes in the exact implementation: + +The lines with `let _tmp = $valf;` and `let _tmp = $val;` are replaced with +`let _tmp = WrapDebug($valf);` and `let _tmp = WrapDebug($val);`. The lines +with `_tmp` are replaced with `_tmp.0`. + # Drawbacks [drawbacks]: #drawbacks @@ -995,62 +1068,108 @@ expression. ## Formerly unresolved questions [formerly unresolved]: #formerly-unresolved-questions -Some questions regarding the format were: +The following section gives a overview of certain design decisions taken during +the RFC process and a detailed reasoning behind the decisions. These questions +are ordered by when they were introduced. -1. Should the `file!()` be included? +### 1. Should the `file!()` be included? **Yes**, since it would be otherwise difficult to tell where the output is coming from in a larger project with multiple files. It is not very useful on the [playground](https://play.rust-lang.org), but that exception is acceptable. -2. Should the line number be included? +### 2. Should the line number be included? **Yes**, for a large file, it would also be difficult to locate the source of the output otherwise. -3. Should the column number be included? +### 3. Should the column number be included? -It is more likely than not that no more than one `dbg!(...)` will occur. If it -does, it will most likely be when dealing with binary operators such as with: -`dbg!(x) + dbg!(y) + dbg!(z)`, or with several arguments to a function/method -call. However, since the macro prints out `stringify!(expr)`, which in the case -of the additions would result in: `x = , y = , z = `, the user -can clearly see which expression on the line that generated the value. The only -exception to this is if the same expression is used multiple times and crucically -has side effects altering the value between calls. This scenario is probably -very uncommon. Furthermore, even in this case, one can distinguish between the -calls since one is first and the second comes next, visually. +**No.** It is more likely than not that no more than one `dbg!(...)` will occur. +If it does, it will most likely be when dealing with binary operators such as +with: `dbg!(x) + dbg!(y) + dbg!(z)`, or with several arguments to a +function / method call. However, since the macro prints out `stringify!(expr)`, +which in the case of the additions would result in: +`x = , y = , z = `, the user can clearly see which expression on +the line that generated the value. The only exception to this is if the same +expression is used multiple times and crucically has side effects altering the +value between calls. This scenario is probably very uncommon. Furthermore, even +in this case, one can distinguish between the calls since one is first and the +second comes next, visually. However, the `column!()` isn't very visually disturbing since it uses horizontal screen real-estate but not vertical real-estate, which may still be a good reason to keep it. Nonetheless, this argument is not sufficient to keep `column!()`, wherefore **this RFC will not include it**. -4. Should the `stringify!($val)` be included? +### 4. Should the `stringify!($val)` be included? **Yes**, it helps the user see the source of the value printed. -Other, now resolved, questions were: +### 5. Should the macro be pass-through with respect to the expression? -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? +In other words: should the value of applying the macro to the expression be the +value of the expression? **Yes**, the pass-through mechanism allows the macro to be less intrusive as discussed in the [motivation]. -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. +### 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. **Yes**, since some users who develop programs, and not libraries, can leave such `dbg!(..)` invocations in and push it to source control since it won't affect debug builds of the program. -8. Should a trailing newline be added after each `dbg!(exprs...)`? +[`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 +[`debugit`]: https://docs.rs/debugit/0.1.2/debugit/ + +### 7. Should the macro accept expressions where: `![typeof(expr) : Debug]`? + +In other words, should expressions and values of non-`Debug` types be accepted +by the macro via [`specialization`] for `Debug` types? + +**Yes**, and for two reasons: + ++ Avoiding the bound `T: Debug` in generic code. + +To see why, let's consider answering this question with a no. Imagine having +some generic algorithm in your code: + +```rust +fn process_items(iter: I) where I: Iterator { + for elem in iter { /* .. */ } +} +``` + +Now you want inspect the value of `elem` is, so you use `dbg!(elem);`: + +```rust +fn process_items(iter: I) where I: Iterator { + for elem in iter { + dbg!(elt); + // .. + } +} +``` -**Short answer: Yes.** +However, since doing `dbg!(elem)` requires that `I::Item : Debug`, you can't. +If you add the `Debug` bound, you'll also need to add it to any function, where +`Item` is held generic, which calls `process_items`, and transitively, you may +need add the bound to several function calls up the stack. Doing such a change +is not ergonomic as it may require you to even jump through different files. +With [`specialization`], you can instead use the `Debug` trait implicitly. -The result of answer in the negative would use the following format: ++ Some information is better than none. + +Even if `typeof(expr)` is `!Debug`, valuable information can be displayed to the +user. By using `std::intrinsics::type_name` for non-`Debug` types, the user can +at least know what the type of the expression is, which is not nothing. + +### 8. Should a trailing newline be added after each `dbg!(exprs...)`? + +**Yes.** The result of answer in the negative would use the following format: ``` [DEBUGGING, src/main.rs:85] @@ -1098,15 +1217,14 @@ example as: [src/main.rs:87] a = 1, b = 2, a + b = 3 ``` -9. Should debugging literal expressions as in `dbg!(42);` print out `42 = 42` or -should it print out `5`? +### 9. Should literals used in `dbg!(lit);` print out `lit = lit` or `lit`? **No**. The left hand side of the equality adds no new information wherefore it might be a redundant annoyance. On the other hand, it may give a sense of symmetry with the non-literal forms such as `a = 42`. Keeping `5 = 5` is also more consistent, wherefore that format will be used. -10. Should `dbg!(expr);` generate an "unused" warning? +### 10. Should `dbg!(expr);` generate an "unused" warning? **No**. In the case of: @@ -1121,11 +1239,9 @@ the macro is used in "print" mode instead of "passhrough inspector" mode. Both are expected and supported ways of using this macro wherefore no warning should be raised. -11. Should `STDOUT` be used over `STDERR` as the output stream? - -**Short answer: No.** +### 11. Should `STDOUT` be used over `STDERR` as the output stream? -The messages printed using `dbg!(..)` are not usually errors, which is +**No.** The messages printed using `dbg!(..)` are not usually errors, which is one reason to use `STDOUT` instead. However, `STDERR` is often used as a second channel for extra messages. This use of `STDERR` often occurs when `STDOUT` carries some data which you can't mix with random messages. @@ -1141,12 +1257,11 @@ programmers who just want to debug quickly. For these reasons, `STDERR` should be used. -12. Should `std::panic::catch_unwind` be used to handle panics? +### 12. Should `std::panic::catch_unwind` be used to handle panics? -**Short answer: No.** - -If `expr` in `dbg!("label" => expr)` panics, should something be printed on the RHS of `"label" => ` as in: `"label" => ` ? If so, should all panics be -caught such that: +**No.** If `expr` in `dbg!("label" => expr)` panics, should something be printed +on the RHS of `"label" => ` as in: `"label" => ` ? If so, should all +panics be caught such that: ```rust fn main() { @@ -1189,14 +1304,4 @@ is for very little gain, wherefore this question is too answered in the negative [unresolved]: #unresolved-questions The format used by the macro should be resolved prior to merging. - -[`specialization`]: https://github.com/rust-lang/rfcs/pull/1210 - -To be revisited once [`specialization`] has been stabilized: - -[`debugit`]: https://docs.rs/debugit/0.1.2/debugit/ - -7. Should expressions and values of non-`Debug` types be usable with this macro -by using `std::intrinsics::type_name` for such types and the `Debug` impl for -`T : Debug` types as done in version 0.1.2 of [`debugit`]? This depends on -specialization. \ No newline at end of file +There are currently no unresolved questions. \ No newline at end of file From 98a2492806c75655caf3d6516af8c10785658ba8 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 27 Oct 2017 09:45:56 +0200 Subject: [PATCH 27/30] rfc, quick_debug_macro: simplified macro slightly, no semantic changes --- text/0000-quick-debug-macro.md | 313 +++++++++++++-------------------- 1 file changed, 127 insertions(+), 186 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 83505a0789b..edb487c5e54 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -605,9 +605,6 @@ The `dbg!` macro is semantically (with the notable detail that the helper macros and any non-`pub` `fn`s must be inlined in the actual implementation): ```rust -// For #[allow(unused_parens)]: -#![feature(stmt_expr_attributes)] - pub fn in_detailed_mode() -> bool { option_env!("RUST_DBG_COMPACT").map_or(false, |s| s == "0") } @@ -627,60 +624,54 @@ macro_rules! w { macro_rules! dbg_header { ($err: ident) => { - #[cfg(debug_assertions)] { - if in_detailed_mode() { - w!($err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) - } else { - w!($err, "[{}:{}] ", file!(), line!()) - } + if in_detailed_mode() { + w!($err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) + } else { + w!($err, "[{}:{}] ", file!(), line!()) } }; } macro_rules! dbg_footer { ($err: ident) => { - #[cfg(debug_assertions)] { - if in_detailed_mode() { w!($err, "\n\n") } else { w!($err, "\n") } - } + if in_detailed_mode() { w!($err, "\n\n") } else { w!($err, "\n") } }; } macro_rules! dbg_expr { ($err: ident, $lab: expr => $val: expr) => {{ - #[cfg(debug_assertions)] { w!($err, "{} = ", $lab) } + w!($err, "{} = ", $lab); let _tmp = $val; - #[cfg(debug_assertions)] { - if in_detailed_mode() { w!($err, "{:#?}", _tmp) } - else { w!($err, "{:?}" , _tmp) } - } + if in_detailed_mode() { w!($err, "{:#?}", _tmp) } + else { w!($err, "{:?}" , _tmp) } _tmp }} } macro_rules! dbg_core { - ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { - #[allow(unreachable_code)] // for panics. - #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] - { - #[cfg(debug_assertions)] + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => {{ + #[allow(unreachable_code, unused_must_use, unused_parens)] + let _r = { + #[cfg(not(debug_assertions))] { ($valf $(, $val)*) } + #[cfg(debug_assertions)] { use ::std::io::Write; - #[cfg(debug_assertions)] let stderr = ::std::io::stderr(); - #[cfg(debug_assertions)] let mut err = ::std::io::BufWriter::new(stderr.lock()); dbg_header!(err); let ret = ( dbg_expr!(err, $labf => $valf) $(, { - #[cfg(debug_assertions)] w!(err, ", "); + w!(err, ", "); dbg_expr!(err, $lab => $val) } )* ); dbg_footer!(err); ret } - }; + }; + _r + }}; } #[macro_export] @@ -698,7 +689,7 @@ macro_rules! dbg { }; // With label: ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => {{ - verify_str_lit!($labf, $($lab),*); + verify_str_lit!($labf $(, $lab)*); dbg_core!($labf => $valf $(, $lab => $val)*) }}; } @@ -710,9 +701,6 @@ The exact implementation, which is authoritative on the semantics of this RFC, is given by: ```rust -// For #[allow(unused_parens)]: -#![feature(stmt_expr_attributes)] - #[macro_export] macro_rules! dbg { // Handle trailing comma: @@ -723,39 +711,34 @@ macro_rules! dbg { dbg!( $($lab => $val),+ ) }; // Without label, use source of $val: - ($valf: expr $(, $val: expr)*) => { - #[allow(unreachable_code)] // for panics. - #[allow(unused_must_use)] // for clarification on: dbg!(expr); - #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] - { + ($valf: expr $(, $val: expr)*) => {{ + // in order: for panics, clarification on: dbg!(expr);, dbg!(expr) + #[allow(unreachable_code, unused_must_use, unused_parens)] + let _r = { + #[cfg(not(debug_assertions))] { ($valf $(, $val)*) } + #[cfg(debug_assertions)] { // DEBUG: Lock STDERR in a buffered writer. // Motivation: // 1. to avoid needless re-locking of STDERR at every write(ln)!. // 2. to ensure that the printed message is not interleaved, which // would disturb the readability of the output, by other messages to // STDERR. - #[cfg(debug_assertions)] use ::std::io::Write; - #[cfg(debug_assertions)] let stderr = ::std::io::stderr(); - #[cfg(debug_assertions)] let mut err = ::std::io::BufWriter::new(stderr.lock()); - // Are we in not in compact mode (detailed)? + // Are we in not in detailed mode (compact)? // If so: // + {:?} is used instead of {:#?}, // + Header is: [] - #[cfg(debug_assertions)] let detailed = option_env!("RUST_DBG_COMPACT") .map_or(true, |s| s == "0"); - #[cfg(debug_assertions)] { - (if detailed { - write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) - } else { - write!(&mut err, "[{}:{}] ", file!(), line!()) - }).unwrap() - } + (if detailed { + write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) + } else { + write!(&mut err, "[{}:{}] ", file!(), line!()) + }).unwrap(); // Foreach label and expression: // 1. Evaluate each expression, @@ -763,208 +746,165 @@ macro_rules! dbg { let _ret = ( { // Print out $lab = : - #[cfg(debug_assertions)] { - write!(&mut err, "{} = ", stringify!($valf)).unwrap() - } + write!(&mut err, "{} = ", stringify!($valf)).unwrap(); + // Evaluate, tmp is value: - let _tmp = $valf; // Won't get further if $val panics. + let _tmp = $valf; + // Won't get further if $val panics. + // Print out tmp: - #[cfg(debug_assertions)] { - (if detailed { write!(&mut err, "{:#?}", _tmp) } - else { write!(&mut err, "{:?}" , _tmp) } - ).unwrap() - } + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) }).unwrap(); + // Yield tmp: _tmp } $(, { // Comma separator: - #[cfg(debug_assertions)] { - write!(&mut err, ", ").unwrap(); - } - { - // Print out $lab = : - #[cfg(debug_assertions)] { - write!(&mut err, "{} = ", stringify!($val)).unwrap() - } - // Evaluate, tmp is value: - let _tmp = $val; // Won't get further if $val panics. - // Print out tmp: - #[cfg(debug_assertions)] { - (if detailed { write!(&mut err, "{:#?}", _tmp) } - else { write!(&mut err, "{:?}" , _tmp) } - ).unwrap() - } - // Yield tmp: - _tmp - } + write!(&mut err, ", ").unwrap(); + + // Print out $lab = : + write!(&mut err, "{} = ", stringify!($val)).unwrap(); + + // Evaluate, tmp is value: + let _tmp = $val; + // Won't get further if $val panics. + + // Print out tmp: + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) }).unwrap(); + + // Yield tmp: + _tmp } )* ); // Newline: - #[cfg(debug_assertions)] { - (if detailed { - writeln!(&mut err, "\n") - } else { - writeln!(&mut err, "") - }).unwrap() - } + (if detailed { writeln!(&mut err, "\n") } + else { writeln!(&mut err, "") }).unwrap(); // Return the expression: _ret } - }; + }; + _r + }}; // With label: - ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { - #[allow(unreachable_code)] // for panics. - #[allow(unused_must_use)] // for clarification on: dbg!(expr); - #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] - { + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => {{ + // in order: for panics, clarification on: dbg!(expr);, dbg!(expr) + #[allow(unreachable_code, unused_must_use, unused_parens)] + let _r = { + #[cfg(not(debug_assertions))] { ($valf $(, $val)*) } + #[cfg(debug_assertions)] { // DEBUG: Lock STDERR in a buffered writer. // Motivation: // 1. to avoid needless re-locking of STDERR at every write(ln)!. // 2. to ensure that the printed message is not interleaved, which // would disturb the readability of the output, by other messages to // STDERR. - #[cfg(debug_assertions)] use ::std::io::Write; - #[cfg(debug_assertions)] let stderr = ::std::io::stderr(); - #[cfg(debug_assertions)] let mut err = ::std::io::BufWriter::new(stderr.lock()); - // Are we in not in compact mode (detailed)? + // Are we in not in detailed mode (compact)? // If so: // + {:?} is used instead of {:#?}, // + Header is: [] - #[cfg(debug_assertions)] let detailed = option_env!("RUST_DBG_COMPACT") .map_or(true, |s| s == "0"); - #[cfg(debug_assertions)] { - (if detailed { - write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) - } else { - write!(&mut err, "[{}:{}] ", file!(), line!()) - }).unwrap() - } + (if detailed { + write!(&mut err, "[DEBUGGING, {}:{}]\n=> ", file!(), line!()) + } else { + write!(&mut err, "[{}:{}] ", file!(), line!()) + }).unwrap(); // Foreach label and expression: // 1. Evaluate each expression, // 2. Print out $lab = value of expression let _ret = ( { + // Enforce is_literal_string($lab): + let _ = concat!($labf, ""); + let _ : &'static str = $labf; + // Print out $lab = : - #[cfg(debug_assertions)] { - // Enforce is_literal_string($lab): - let _ = concat!($labf, ""); - let _ : &'static str = $labf; - write!(&mut err, "{} = ", stringify!($labf)).unwrap() - } + write!(&mut err, "{} = ", stringify!($labf)).unwrap(); + // Evaluate, tmp is value: - let _tmp = $valf; // Won't get further if $valf panics. + let _tmp = $valf; + // Won't get further if $val panics. + // Print out tmp: - #[cfg(debug_assertions)] { - (if detailed { write!(&mut err, "{:#?}", _tmp) } - else { write!(&mut err, "{:?}" , _tmp) } - ).unwrap() - } + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) }).unwrap(); + // Yield tmp: _tmp } $(, { // Comma separator: - #[cfg(debug_assertions)] { - write!(&mut err, ", ").unwrap(); - } - { - // Print out $lab = : - #[cfg(debug_assertions)] { - // Enforce is_literal_string($lab): - let _ = concat!($lab, ""); - let _ : &'static str = $lab; - write!(&mut err, "{} = ", stringify!($lab)).unwrap() - } - // Evaluate, tmp is value: - let _tmp = $val; // Won't get further if $valf panics. - // Print out tmp: - #[cfg(debug_assertions)] { - (if detailed { write!(&mut err, "{:#?}", _tmp) } - else { write!(&mut err, "{:?}" , _tmp) } - ).unwrap() - } - // Yield tmp: - _tmp - } + write!(&mut err, ", ").unwrap(); + + // Enforce is_literal_string($lab): + let _ = concat!($lab, ""); + let _ : &'static str = $lab; + + // Print out $lab = : + write!(&mut err, "{} = ", stringify!($lab)).unwrap(); + + // Evaluate, tmp is value: + let _tmp = $val; + // Won't get further if $val panics. + + // Print out tmp: + (if detailed { write!(&mut err, "{:#?}", _tmp) } + else { write!(&mut err, "{:?}" , _tmp) }).unwrap(); + + // Yield tmp: + _tmp } )* ); // Newline: - #[cfg(debug_assertions)] { - (if detailed { - writeln!(&mut err, "\n") - } else { - writeln!(&mut err, "") - }).unwrap() - } + (if detailed { writeln!(&mut err, "\n") } + else { writeln!(&mut err, "") }).unwrap(); // Return the expression: _ret } - }; + }; + _r + }}; } ``` On release builds, this macro reduces to: ```rust -// For #[allow(unused_parens)]: -#![feature(stmt_expr_attributes)] - #[macro_export] macro_rules! dbg { - // ... - - // Without label, use source of $val: - ($valf: expr $(, $val: expr)*) => { - #[allow(unused_must_use)] // for clarification on: dbg!(expr); - #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] - { - let ret = ( - { - let tmp = $valf; - tmp - } - $(, { - { - let tmp = $val; - tmp - } - } )* - ); - ret - } + // Handle trailing comma: + ($($val: expr),+,) => { + dbg!( $($val),+ ) }; - // With label: - ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => { - #[allow(unused_must_use)] // for clarification on: dbg!(expr); - #[allow(unused_parens)] // requires: #![feature(stmt_expr_attributes)] - { - let ret = ( - { - let tmp = $valf; - tmp - } - $(, { - { - let tmp = $val; - tmp - } - } )* - ); - ret - } + ($($lab: expr => $val: expr),+,) => { + dbg!( $($lab => $val),+ ) }; + // Without label, use source of $val: + ($valf: expr $(, $val: expr)*) => {{ + // in order: for panics, clarification on: dbg!(expr);, dbg!(expr) + #[allow(unreachable_code, unused_must_use, unused_parens)] + let _r = {{ ($valf $(, $val)*) }}; + _r + }}; + // With label: + ($labf: expr => $valf: expr $(, $lab: expr => $val: expr)*) => {{ + // in order: for panics, clarification on: dbg!(expr);, dbg!(expr) + #[allow(unreachable_code, unused_must_use, unused_parens)] + let _r = {{ ($valf $(, $val)*) }}; + _r + }}; } ``` @@ -974,7 +914,8 @@ is nothing more than the identity on the tuple passed: ```rust #[macro_export] macro_rules! dbg { - // ... + ($($val: expr),+,) => { dbg!( $($val),+ ) }; + ($($lab: expr => $val: expr),+,) => { dbg!( $($lab => $val),+ ) }; ($( $val: expr),+) => {{ ( $($val),* ) }}; ($($lab: expr => $val: expr),+) => {{ ( $($val),* ) }}; } From d2d2f3be3b0940e13310aa76037562fcdd6d813f Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 19 Jan 2018 04:35:59 +0100 Subject: [PATCH 28/30] rfc, quick_debug_macro: allow dbg!(); + internal consistency fixes + linguistic improvements --- text/0000-quick-debug-macro.md | 92 ++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index edb487c5e54..f238f295401 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -6,9 +6,9 @@ # Summary [summary]: #summary -Adds a macro `dbg!(expr1 [, expr2, .., exprN])` for quick and dirty `Debug`:ing +Adds a macro `dbg!([expr1 , expr2, .., exprN])` for quick and dirty `Debug`ing of expressions to the terminal. The macro evaluates expressions, prints it to -`STDERR`, and finally yields a flat tuple of `(expr1 [, expr2, .. exprN])`. +`STDERR`, and finally yields a flat tuple of `([expr1, expr2, .. exprN])`. 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. @@ -45,11 +45,11 @@ let c = fun(a) + fun(b); let y = self.first().second(); ``` -Now you want to inspect what `fun(a)` and `fun(b)` is, but not go through the +Now, you want to inspect what `fun(a)` and `fun(b)` is, but not go through the hassle of 1) saving `fun(a)` and `fun(b)` to a variable, 2) printing out the variable, 3) then finally use it in the expression as `let c = fa + fb;`. -The same applies to inspecting the temporary state of `self.first()`. Instead of -this hassle, you can simply do: +The same applies to inspecting the temporary state of `self.first()`. +Instead of this hassle, you can simply do: ```rust let c = dbg!(fun(a)) + dbg!(fun(b)); @@ -210,22 +210,24 @@ example, the following is printed to `STDERR`: } ``` +The ways of using the macro in illustrated in later (not the first) examples +will mostly benefit existing Rust programmers. + +### Move semantics + It is important to note here that since the type `Point` is not `Copy`, it has move semantics. Since `dbg!(p)` would involve moving `p`, using `dbg!(p, p);` would involve moving the value twice, which Rust will not allow. Therefore, a borrow to `p` is used in `dbg!("first point" => &p, "second point" => &p);`. -The ways of using the macro used in later (not the first) examples will mostly -benefit existing Rust programmers. - ### Compact mode: Those developers who feel the source location header is overly verbose may choose to opt-out by setting the environment variable `RUST_DBG_COMPACT` to -`"0"`. This is a one-time setup cost the developer has to make for all current +`"1"`. This is a one-time setup cost the developer has to make for all current and future Rust projects. -The effect of flipping this switch off is to print out the following instead +The effect of flipping this switch on is to print out the following instead for the two last examples in [on-debug-builds]: ``` @@ -277,6 +279,31 @@ fail-fast on any panic in order to avoid cascading panics and unsafety. The same examples above will print nothing to `STDERR` and will instead simply evaluate the expressions. +## Calling `dbg!()` without any expressions + +If you invoke the macro without providing any expressions as arguments, the +macro will treat this as if you passed the unit value `()` from which it +follow that the type will be the unit type. + +Doing this can be useful if you want to ensure that a path is taken in +some conditional flow. An example: + +```rust +fn main() { + // assume we have: `some_important_conditional: bool` defined elsewhere. + if some_important_conditional { + dbg!(); + } +} +``` + +which may produce the following if `some_important_conditional` holds: + +``` +[DEBUGGING, src\lib.rs:4] +=> () = () +``` + ## Types which are not `Debug` **This feature will be available once [`specialization`] has been stabilized @@ -312,7 +339,7 @@ the following is printed to `STDERR`: => &a = [ of type &main::X is !Debug] ``` -This tells you the typeof `&a`, and that it is not `Debug`. +This tells you the type of `&a`, and that it is not `Debug`. ## An example from the real world @@ -398,7 +425,8 @@ or prints, with `RUST_DBG_COMPACT = 1`: [src/main.rs:9] factorial(4) = 24 ``` -But you like labels, so you use them instead: +But you prefer labels, since you think they are more informative, +and use them instead: ```rust fn factorial(n: u32) -> u32 { @@ -519,8 +547,8 @@ or prints, with `RUST_DBG_COMPACT = 1`: **NOTE:** The exact output format is not meant to be stabilized even when/if the macro is stabilized. -The macro is called `dbg` and accepts either a non-empty comma-separated or -comma-terminated list of `expr`, or a non-empty list of `label => expr` which +The macro is called `dbg` and accepts either a comma-separated or +comma-terminated list of `expr`, or a list of `label => expr` which is also separated or terminated with commas. The terminated versions are defined as: @@ -533,6 +561,8 @@ The separated versions accept the following: 1. `($($val: expr),+)` 2. `($($lab: expr => $val: expr),+)` +Finally, the macro can be called as `dbg!()`. + The macro only prints something if `cfg!(debug_assertions)` holds, meaning that if the program is built as a release build, nothing will be printed, and the result of using the macro on an expressions or expressions is simply the @@ -542,10 +572,12 @@ be inlined away such that the overhead is zero. ## The type of `dbg!(expressions)` -"Applying" `dbg` on a non-empty list of expressions -`expr1 [, expr2 [, .., exprN])` gives back an expression of the following type +"Applying" `dbg` on a list of expressions +`[expr1, expr2 [, .., exprN]` gives back an expression of the following type and value: ++ List of size 0, `dbg!()`: The type is the unit type `()`. + + List of size 1, `dbg!(expr)`: The type is the type of `expr` and the value is the value of `expr`. @@ -572,6 +604,10 @@ If `p` does not hold, this instead prints: write!(&mut err, "[{}:{}] ", file!(), line!()) ``` +3. + For `()` + +Defined as `dbg!(())`. + 3. + For `($($val: expr),+)` For each `$val` (the expression), the following is printed, comma separated: @@ -676,6 +712,10 @@ macro_rules! dbg_core { #[macro_export] macro_rules! dbg { + // Handle `dbg!()` <-- literal + () => { + dbg!( () ); + }; // Handle trailing comma: ($($val: expr),+,) => { dbg!( $($val),+ ) @@ -703,6 +743,10 @@ is given by: ```rust #[macro_export] macro_rules! dbg { + // Handle `dbg!()` <-- literal + () => { + dbg!( () ); + }; // Handle trailing comma: ($($val: expr),+,) => { dbg!( $($val),+ ) @@ -884,6 +928,10 @@ On release builds, this macro reduces to: ```rust #[macro_export] macro_rules! dbg { + // Handle `dbg!()` <-- literal + () => { + dbg!( () ); + }; // Handle trailing comma: ($($val: expr),+,) => { dbg!( $($val),+ ) @@ -914,6 +962,7 @@ is nothing more than the identity on the tuple passed: ```rust #[macro_export] macro_rules! dbg { + () => { dbg!( () ); }; ($($val: expr),+,) => { dbg!( $($val),+ ) }; ($($lab: expr => $val: expr),+,) => { dbg!( $($lab => $val),+ ) }; ($( $val: expr),+) => {{ ( $($val),* ) }}; @@ -1104,9 +1153,10 @@ With [`specialization`], you can instead use the `Debug` trait implicitly. + Some information is better than none. -Even if `typeof(expr)` is `!Debug`, valuable information can be displayed to the -user. By using `std::intrinsics::type_name` for non-`Debug` types, the user can -at least know what the type of the expression is, which is not nothing. +Even if the type of `expr` does not satisfy the `Debug` bound, valuable +information can be displayed to the user. By using `std::intrinsics::type_name` +for non-`Debug` types, the user can at least know what the type of the +expression is, which is not nothing. ### 8. Should a trailing newline be added after each `dbg!(exprs...)`? @@ -1149,7 +1199,7 @@ However, it is more unlikely that a user will see the information they are looking for in a small window without scrolling. Here, searchability is aided by grouping which is visually pleasing to process. -This was resolved by having the env var `RUST_DBG_COMPACT = 0` format the above +This was resolved by having the env var `RUST_DBG_COMPACT = 1` format the above example as: ``` @@ -1158,7 +1208,7 @@ example as: [src/main.rs:87] a = 1, b = 2, a + b = 3 ``` -### 9. Should literals used in `dbg!(lit);` print out `lit = lit` or `lit`? +### 9. Should literals used in `dbg!(lit);` print out `lit` instead of `lit = lit`? **No**. The left hand side of the equality adds no new information wherefore it might be a redundant annoyance. On the other hand, it may give a sense of From 354870c348d8e79c76296507b388a421980ee845 Mon Sep 17 00:00:00 2001 From: Mazdak Date: Fri, 19 Jan 2018 05:25:44 +0100 Subject: [PATCH 29/30] rfc, quick_debug_macro: added motivation: the macro is not very useful outside std --- text/0000-quick-debug-macro.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index f238f295401..9bdbb63b92f 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -68,6 +68,19 @@ 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. +## Not useful as a crate + +This RFC is for quick and dirty debugging and has to be in the standard library +to be useful. Why? Because while the utility provided by the `dbg!` macro is +necessary, it is unlikely that a developer, and that includes the author of +the RFC, would take a dependency on a crate that just provides the macro. The +hassle of constantly re-adding such a crate temporarily would also be greater +than just using `println!("{:#?}", expr);` in a majority of cases. But using +`println!` is still a paper cut. Furthermore, `dbg!` is unlikely to ever be a +crate that you can `extern crate` for in the Rust playground, so the macro +can't be used to quickly help users on `#rust` and other venues, which is one +of the goals of the macro. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 5d0a0c9c1555b0cf3e4ae663f1c9bc50582a5003 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 29 Jan 2018 15:36:33 +0100 Subject: [PATCH 30/30] rfc, quick_debug_macro: fixed typo; thanks @xftroxgpx --- text/0000-quick-debug-macro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-quick-debug-macro.md b/text/0000-quick-debug-macro.md index 9bdbb63b92f..c4fbecf9b34 100644 --- a/text/0000-quick-debug-macro.md +++ b/text/0000-quick-debug-macro.md @@ -223,7 +223,7 @@ example, the following is printed to `STDERR`: } ``` -The ways of using the macro in illustrated in later (not the first) examples +The ways of using the macro as illustrated in later (not the first) examples will mostly benefit existing Rust programmers. ### Move semantics