1- - Feature Name: dbg_macro
1+ - Feature Name: ` dbg_macro `
22- Start Date: 2018-03-13
33- RFC PR:
44- Rust Issue:
@@ -42,7 +42,7 @@ This RFC improves some aspects:
4242[ guide-level-explanation ] : #guide-level-explanation
4343
4444To inspect the value of a given expression at run-time,
45- it can be wrapped in the ` dbg! ` macro to print the value to ` stderr ` ,
45+ it can be wrapped in the ` dbg! ` macro to print the value to ` STDERR ` ,
4646along with its source location and source code:
4747
4848``` rust
@@ -54,13 +54,55 @@ fn foo(n: usize) {
5454
5555foo (3 )
5656```
57+
58+ This prints the following to ` STDERR ` :
59+
5760```
5861[example.rs:2] n.checked_sub(4) = None
5962```
6063
61- This requires the type of the expression to implement the ` std::fmt::Debug ` trait.
62- The macro moves the value (takes ownership of it, unless its type implements ` Copy ` )
63- and returns it unchanged.
64+ Another example is ` factorial ` which we can debug like so:
65+
66+ ``` rust
67+ fn factorial (n : u32 ) -> u32 {
68+ if dbg! (n <= 1 ) {
69+ dbg! (1 )
70+ } else {
71+ dbg! (n * factorial (n - 1 ))
72+ }
73+ }
74+
75+ fn main () {
76+ dbg! (factorial (4 ));
77+ }
78+ ```
79+
80+ Running this program, in the playground, will print the following to ` STDERR ` :
81+
82+ ```
83+ [src/main.rs:1] n <= 1 = false
84+ [src/main.rs:1] n <= 1 = false
85+ [src/main.rs:1] n <= 1 = false
86+ [src/main.rs:1] n <= 1 = true
87+ [src/main.rs:2] 1 = 1
88+ [src/main.rs:4] n * factorial(n - 1) = 2
89+ [src/main.rs:4] n * factorial(n - 1) = 6
90+ [src/main.rs:4] n * factorial(n - 1) = 24
91+ [src/main.rs:9] factorial(4) = 24
92+ ```
93+
94+ Using ` dbg! ` requires type of the expression to implement the ` std::fmt::Debug `
95+ trait.
96+
97+ ## Move semantics
98+
99+ The ` dbg!(x) ` macro moves the value ` x ` and takes ownership of it,
100+ unless the type of ` x ` implements ` Copy ` , and returns ` x ` unchanged.
101+ If you want to retain ownership of the value,
102+ you can instead borrow ` x ` with ` dbg!(&x) ` .
103+
104+ ## Unstable output format
105+
64106The exact output printed by this macro should not be relied upon and is subject to future changes.
65107
66108
@@ -85,10 +127,9 @@ macro_rules! dbg {
85127 }
86128 }
87129}
88-
89130```
90131
91- The use of ` match ` over let is similar to the implementation of ` assert_eq! ` .
132+ The use of ` match ` over ` let ` is similar to the implementation of ` assert_eq! ` .
92133It [ affects the lifetimes of temporaries] (
93134https://stackoverflow.com/questions/48732263/why-is-rusts-assert-eq-implemented-using-a-match#comment84465322_48732525 ).
94135
@@ -98,40 +139,158 @@ https://stackoverflow.com/questions/48732263/why-is-rusts-assert-eq-implemented-
98139Adding to the prelude should be done carefully.
99140However a library can always define another macro with the same name and shadow this one.
100141
101- # Alternatives
142+ # Rationale and alternatives
102143[ alternatives ] : #alternatives
103144
104- - See [ RFC 2173] ( https://github.com/rust-lang/rfcs/pull/2173 ) and discussion there.
145+ [ RFC 2173] and provides an a more complex alternative that offers more control but is also more complex.
146+ This RFC was designed with the goal of being a simpler and thus better fit for the standard library.
147+
148+ ## Alternative: tweaking formatting
149+
150+ Any detail of the formatting can be tweaked. For example, ` {:#?} ` or ` {:?} ` ?
151+
152+ ## A simple macro without any control over output
153+
154+ This RFC does not offer users control over the exact output being printed.
155+ This is because a use of this macro is intended to be run a small number of times before being removed.
156+ If more control is desired, for example logging in an app shipped to end users,
157+ other options such as ` println! ` or the ` log ` crate remain available.
158+
159+ ## Accepting a single expression instead of many
160+
161+ If the macro accepts more than one expression (returning a tuple),
162+ there is a question of what to do with a single expression.
163+ Returning a one-value tuple ` ($expr,) ` is probably unexpected,
164+ but * not* doing so creates a discontinuty in the macro's behavior as things are added.
165+ With only one expression accepted,
166+ users can still pass a tuple expression or call the macro multiple times.
167+
168+ ## Including ` file!() ` in the output
169+
170+ In a large project with multiple files,
171+ it becomes quite difficult to tell what the origin of the output is.
172+ Including ` file!() ` is therefore quite helpful in debugging.
173+ However, it is not very useful on the [ playground] ( https://play.rust-lang.org ) ,
174+ but that exception is acceptable.
175+
176+ ## Including the line number
105177
106- - This RFC does not offer users control over the exact output being printed.
107- This is because a use of this macro is intended to be run a small number of times
108- before being removed.
109- If more control is desired (for example logging in an app shipped to end users),
110- other options like ` println! ` or the ` log ` crate remain available.
178+ The argument is analogous to that for ` file!() ` . For a large file,
179+ it would also be difficult to locate the source of the output without ` line!() ` .
111180
112- - If the macro accepts more than one expression (returning a tuple),
113- there is a question of what to do with a single expression.
114- Returning a one-value tuple ` ($expr,) ` is probably unexpected,
115- but * not* doing so creates a discontinuty in the macro’s behavior as things are added.
116- With only one expression accepted, users can still pass a tuple expression
117- or call the macro multiple times.
181+ ## Excluding the column number
118182
119- - Printing could be disabled when ` cfg!(debug_assertions) ` is false to reduce runtime cost
120- in release build.
121- However this cost is not relevant if uses of ` dbg! ` are removed before shipping
122- to any form of production (where the ` log ` crate might be better suited)
123- and deemed less important than the ability to easily investigate bugs
124- that only occur with optimizations.
125- (Which [ do happen] ( https://github.com/servo/servo/issues/19519 )
126- and can be a pain to debug.)
183+ Most likely, only one ` dbg!(expr) ` call will occur per line.
184+ The remaining cases will likely occur when dealing with binary operators such as with:
185+ ` dbg!(x) + dbg!(y) + dbg!(z) ` , or with several arguments to a function / method call.
186+ However, since the macro prints out ` stringify!(expr) ` ,
187+ the user can clearly see which expression on the line that generated the value.
188+ The only exception to this is if the same expression is used multiple times and
189+ crucically has side effects altering the value between calls.
190+ This scenario is probably uncommon.
191+ Furthermore, even in this case, one can visually distinguish between the calls
192+ since one is first and the second comes next.
127193
128- - Any detail of the formatting can be tweaked. For example, ` {:#?} ` or ` {:?} ` ?
194+ Another reason to exclude ` column!() ` is that we want to keep the macro simple, and thus,
195+ we only want to keep the essential parts that help debugging most.
196+
197+ However, the ` column!() ` isn't very visually disturbing
198+ since it uses horizontal screen real-estate but not vertical real-estate,
199+ which may still be a good reason to keep it.
200+ Nonetheless, this argument is not sufficient to keep ` column!() ` ,
201+ wherefore ** this RFC will not include it** .
202+
203+ ## Including ` stringify!(expr) `
204+
205+ As discussed in the rationale regarding ` column!() ` ,
206+ ` stringify!(expr) ` improves the legibility of similar looking expressions.
207+
208+ Another major motivation is that with many outputs,
209+ or without all of the source code in short term memory,
210+ it can become hard to associate the printed output with the logic as you wrote it.
211+ With ` stringify! ` , you can easily see how the left-hand side reduces to the right-hand side.
212+ This makes it easier to reason about the trace of your program and why things happened as they did.
213+ The ability to trace effectively can greatly improve the ability to debug with ease and speed.
214+
215+ ## Returning the value that was given
216+
217+ One goal of the macro is to intrude and disturb as little as possible in the workflow of the user.
218+ The macro should fit the user, not the other way around.
219+ Returning the value that was given, i.e: that ` dbg!(expr) == expr `
220+ and ` typeof(expr) == typeof(dbg!(expr)) ` allows just that.
221+
222+ To see how writing flow is preserved, consider starting off with:
223+
224+ ``` rust
225+ let c = fun (a ) + fun (b );
226+ let y = self . first (). second ();
227+ ```
228+
229+ Now, you want to inspect what ` fun(a) ` and ` fun(b) ` evaluates to.
230+ But you would like to avoid going through the hassle of:
231+
232+ 1 . saving ` fun(a) ` and ` fun(b) ` to a variable
233+ 2 . printing out the variable
234+ 3 . using it in the expression as ` let c = fa + fb; ` .
235+
236+ The same logic applies to inspecting the temporary state of ` self.first() ` .
237+ Instead of the hassle, you can simply do:
238+
239+ ``` rust
240+ let c = dbg! (fun (a )) + dbg! (fun (b ));
241+ let y = dbg! (self . first ()). second ();
242+ ```
243+
244+ This modification is considerably smaller and disturbs flow while debugging code to a lesser degree.
245+
246+ ## Keeping output when ` cfg!(debug_assertions) ` is disabled
247+
248+ When ` cfg!(debug_assertions) ` is false,
249+ printing could be disabled to reduce runtime cost in release builds.
250+ However this cost is not relevant if uses of ` dbg! ` are removed before shipping to production,
251+ where crates such as ` log ` may be better suited,
252+ and deemed less important than the ability to easily investigate bugs that only occur with optimizations.
253+ These kinds of bugs [ do happen] ( https://github.com/servo/servo/issues/19519 ) and can be a pain to debug.
254+
255+ ## ` STDERR ` should be used over ` STDOUT ` as the output stream
256+
257+ The messages printed using ` dbg! ` are not usually errors,
258+ which is one reason to use ` STDOUT ` instead.
259+ However, ` STDERR ` is often used as a second channel for extra messages.
260+ This use of ` STDERR ` often occurs when ` STDOUT ` carries some data which you can't mix with random messages.
261+
262+ If we consider a program such as ` ripgrep ` ,
263+ where should hypothetical uses of ` dbg! ` print to in the case of ` rg some_word < input_file > matching_lines ` ?
264+ Should they end up on the terminal or in the file ` matching_lines ` ?
265+ Clearly the former is correct in this case.
266+
267+ One could say that this design is a lousy choice by the programmer
268+ and that debug messages should be logged to a file,
269+ but this macro must cater to "lousy" programmers who just want to debug quickly.
270+
271+ ## Outputting ` lit = lit ` for ` dbg!(lit); ` instead of ` lit `
272+
273+ The left hand side of the equality adds no new information wherefore it might be a redundant annoyance.
274+ On the other hand, it may give a sense of symmetry with the non-literal forms such as ` a = 42 ` .
275+ Keeping ` 5 = 5 ` is also more consistent.
276+ In either case, since the macro is intentionally simple,
277+ there is little room for tweaks such as removing ` lit = ` .
278+ For these reasons, and especially the last one, the output format ` lit = lit ` is used.
129279
130280# Prior art
131281[ prior-art ] : #prior-art
132282
133283Many languages have a construct that can be as terse as ` print foo ` .
134284
285+ Some examples are:
286+ + [ Haskell] ( http://hackage.haskell.org/package/base-4.10.1.0/docs/Prelude.html#v:print )
287+ + [ python] ( https://docs.python.org/2/library/pprint.html> )
288+ + [ PHP] ( http://php.net/manual/en/function.print-r.php )
289+
290+ [ `traceShowId` ] : http://hackage.haskell.org/package/base-4.10.1.0/docs/Debug-Trace.html#v:traceShowId
291+
292+ The specific idea to return back the input ` expr ` in ` dbg!(expr) ` was inspired by [ ` traceShowId ` ] in Haskell.
293+
135294# Unresolved questions
136295[ unresolved ] : #unresolved-questions
137296
0 commit comments