|
| 1 | +- Feature Name: dbg_macro |
| 2 | +- Start Date: 2018-03-13 |
| 3 | +- RFC PR: |
| 4 | +- Rust Issue: |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +Add a `dbg!($expr)` macro to the prelude (so that it doesn’t need to be imported) |
| 10 | +that prints its argument with some metadata (source code location and stringification) |
| 11 | +before returning it. |
| 12 | + |
| 13 | +This is a simpler and more opinionated counter-proposal |
| 14 | +to [RFC 2173](https://github.com/rust-lang/rfcs/pull/2173). |
| 15 | + |
| 16 | + |
| 17 | +# Motivation |
| 18 | +[motivation]: #motivation |
| 19 | + |
| 20 | +Sometimes a debugger may not have enough Rust-specific support to introspect some data |
| 21 | +(such as calling a Rust method), or it may not be convenient to use or available at all. |
| 22 | +“`printf` debugging” is possible in today’s Rust with: |
| 23 | + |
| 24 | +```rust |
| 25 | +println!("{:?}", expr); |
| 26 | +``` |
| 27 | + |
| 28 | +This RFC improves some aspects: |
| 29 | + |
| 30 | +* The `"{:?}",` part of this line is boilerplate that’s not trivial to remember |
| 31 | + or even type correctly. |
| 32 | +* If the expression to be inspected is part of a larger expression, |
| 33 | + it either needs to be duplicated (which may add side-effects or computation cost) |
| 34 | + or pulled into a `let` binding which adds to the boilerplate. |
| 35 | +* When more than one expression is printed at different places of the same program, |
| 36 | + and the formatting itself (for example a plain integer) |
| 37 | + doesn’t indicate what value is being printed, |
| 38 | + some distinguishing information may need to be added. |
| 39 | + For example: `println!("foo = {:?}", x.foo());` |
| 40 | + |
| 41 | +# Guide-level explanation |
| 42 | +[guide-level-explanation]: #guide-level-explanation |
| 43 | + |
| 44 | +To 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`, |
| 46 | +along with its source location and source code: |
| 47 | + |
| 48 | +```rust |
| 49 | +fn foo(n: usize) { |
| 50 | + if let Some(_) = dbg!(n.checked_sub(4)) { |
| 51 | + /*…*/ |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +foo(3) |
| 56 | +``` |
| 57 | +``` |
| 58 | +[example.rs:2] n.checked_sub(4) = None |
| 59 | +``` |
| 60 | + |
| 61 | +This requires the type of the expression to implement the `std::fmt::Debug` trait. |
| 62 | +The value is returned by the macro unchanged. |
| 63 | + |
| 64 | + |
| 65 | +# Reference-level explanation |
| 66 | +[reference-level-explanation]: #reference-level-explanation |
| 67 | + |
| 68 | +In `src/libstd/lib.rs`, `dbg` is added to the `#[macro_reexport]` attribute on `extern crate core`. |
| 69 | + |
| 70 | +The macro below is added to `src/libcore/macros.rs`, |
| 71 | +with a doc-comment based on the [Guide-level explanation][guide-level-explanation] of this RFC. |
| 72 | + |
| 73 | +```rust |
| 74 | +#[macro_export] |
| 75 | +macro_rules! dbg { |
| 76 | + ($expr:expr) => { |
| 77 | + match $expr { |
| 78 | + expr => { |
| 79 | + eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!($expr), &expr); |
| 80 | + expr |
| 81 | + } |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +``` |
| 87 | + |
| 88 | +The use of `match` over let is similar to the implementation of `assert_eq!`. |
| 89 | +It [affects the lifetimes of temporaries]( |
| 90 | +https://stackoverflow.com/questions/48732263/why-is-rusts-assert-eq-implemented-using-a-match#comment84465322_48732525). |
| 91 | + |
| 92 | +# Drawbacks |
| 93 | +[drawbacks]: #drawbacks |
| 94 | + |
| 95 | +Adding to the prelude should be done carefully. |
| 96 | +However a library can always define another macro with the same name and shadow this one. |
| 97 | + |
| 98 | +# Alternatives |
| 99 | +[alternatives]: #alternatives |
| 100 | + |
| 101 | +- See [RFC 2173](https://github.com/rust-lang/rfcs/pull/2173) and discussion there. |
| 102 | + |
| 103 | +- This RFC does not offer users control over the exact output being printed. |
| 104 | + This is because a use of this macro is intended to be run a small number of times |
| 105 | + before being removed. |
| 106 | + If more control is desired (for example logging in an app shipped to end users), |
| 107 | + other options like `println!` or the `log` crate remain available. |
| 108 | + |
| 109 | +- If the macro accepts more than one expression (returning a tuple), |
| 110 | + there is a question of what to do with a single expression. |
| 111 | + Returning a one-value tuple `($expr,)` is probably unexpected, |
| 112 | + but *not* doing so creates a discontinuty in the macro’s behavior as things are added. |
| 113 | + With only one expression accepted, users can still pass a tuple expression |
| 114 | + or call the macro multiple times. |
| 115 | + |
| 116 | +- Printing could be disabled when `cfg!(debug_assertions)` is false to reduce runtime cost |
| 117 | + in release build. |
| 118 | + However this cost is not relevant if uses of `dbg!` are removed before shipping |
| 119 | + to any form of production (where the `log` crate might be better suited) |
| 120 | + and deemed less important than the ability to easily investigate bugs |
| 121 | + that only occur with optimizations. |
| 122 | + (Which [do happen](https://github.com/servo/servo/issues/19519) |
| 123 | + and can be a pain to debug.) |
| 124 | + |
| 125 | +- Any detail of the formatting can be tweaked. For example, `{:#?}` or `{:?}`? |
| 126 | + |
| 127 | +# Prior art |
| 128 | +[prior-art]: #prior-art |
| 129 | + |
| 130 | +Many languages have a construct that can be as terse as `print foo`. |
| 131 | + |
| 132 | +# Unresolved questions |
| 133 | +[unresolved]: #unresolved-questions |
| 134 | + |
| 135 | +Unbounded bikeshedding. |
0 commit comments