From 850dc4eb3bdddd0ce77e37147b21ed7454ea3de7 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 24 Nov 2017 18:10:07 +0100 Subject: [PATCH 1/4] Hexadecimal integers with fmt::Debug, including within larger types ```rust println!("{:02X?}", b"AZaz\0") ``` ``` [41, 5A, 61, 7A, 00] ``` --- text/0000-fmt-debug-hex.md | 158 +++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 text/0000-fmt-debug-hex.md diff --git a/text/0000-fmt-debug-hex.md b/text/0000-fmt-debug-hex.md new file mode 100644 index 00000000000..032d9d44b28 --- /dev/null +++ b/text/0000-fmt-debug-hex.md @@ -0,0 +1,158 @@ +- Feature Name: fmt-debug-hex +- Start Date: 2017-11-24 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Add support for formatting integers as hexadecimal with the `fmt::Debug` trait, +including when the occur within larger types. + +```rust +println!("{:02X?}", b"AZaz\0") +``` +``` +[41, 5A, 61, 7A, 00] +``` + +# Motivation +[motivation]: #motivation + +Sometimes the bits that make up an integer are more meaningful than its purely numerical value. +For example, an RGBA color encoded in `u32` with 8 bits per channel is easier to understand +when shown as `00CC44FF` than `13387007`. + +The `std::fmt::UpperHex` and `std::fmt::LowerHex` provide hexadecimal formatting +through `{:X}` and `{:x}` in formatting strings, +but they’re only implemented for plain integer types +and not other types like slices that might contain integers. + +The `std::fmt::Debug` trait (used with `{:?}`) however is intended for +formatting “in a programmer-facing, debugging context”. +It can be derived, and doing so is recommended for most types. + +This RFC proposes adding the missing combination of: + +* Output intended primarily for end-users (`Display`) v.s. for programmers (`Debug`) +* Numbers shown in decimal v.s. hexadecimal + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +In formatting strings like in the `format!` and `println!` macros, +the formatting parameters `x` or `X` − to select lower-case or upper-case hexadecimal − +can now be combined with `?` which select the `Debug` trait. + +For example, `format!("{:X?}", [65280].first())` returns `Some(FF00)`. + +This can also be combined with other formatting parameters. +For example, `format!("{:02X?}", b"AZaz\0")` zero-pads each byte to two hexadecimal digits +and return `[41, 5A, 61, 7A, 00]`. + +An API returning `Vec` might be tested like this: + +```rust +let return_value = foo(bar); +let expected = &[ /* ... */ ][..]; +assert!(return_value == expected, "{:08X?} != {:08X?}", return_value, expected); +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Formatting strings + +The syntax of formatting strings +is [specified with a grammar](https://doc.rust-lang.org/std/fmt/#syntax) +which at the moment is as follows: + +``` +format_string := [ maybe-format ] * +maybe-format := '{' '{' | '}' '}' | +format := '{' [ argument ] [ ':' format_spec ] '}' +argument := integer | identifier + +format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type] +fill := character +align := '<' | '^' | '>' +sign := '+' | '-' +width := count +precision := count | '*' +type := identifier | '' +count := parameter | integer +parameter := argument '$' +``` + +This RFC adds an optional *radix* immediately before *type*: + +``` +format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][radix][type] +radix: 'x' | 'X' +``` + +## `Formatter` API + +Note that `x` and `X` are already valid *types*. +They are only interpreted as a radix when the type is `?`, +since combining them with other types doesn’t make sense. + +This radix is exposed indirectly in two additional methods of `std::fmt::Formatter`: + +```rust +impl<'a> Formatter<'a> { + // ... + + /// Based on the radix and type: 16, 10, 8, or 2. + /// + /// This is mostly useful in `Debug` impls, + /// where the trait itself doesn’t imply a radix. + fn number_radix(&self) -> u32 + + /// true for `X` or `E` + /// + /// This is mostly useful in `Debug` impls, + /// where the trait itself doesn’t imply a case. + fn number_uppercase(&self) -> bool +} +``` + +Although the radix and type are separate in the formatting string grammar, +they are intentionally conflated in this new API. + +## `Debug` impls + +The `Debug` implementation for primitive integer types `{u,i}{8,16,32,64,128,size}` +is modified to defer to `LowerHex` or `UpperHex` instead of `Display`, +based on `formatter.number_radix()` and `formatter.number_uppercase()`. +The *alternate* `#` flag is ignored, since it already has a separate meaning for `Debug`: +the `0x` prefix is *not* included. + +As of Rust 1.22, impls using the `Formatter::debug_*` methods do not forward +formatting parameters such as *width* when formatting keys/values/items. +Doing so is important for this RFC to be useful. +This is fixed by [PR #46233](https://github.com/rust-lang/rust/pull/46233). + +# Drawbacks +[drawbacks]: #drawbacks + +The hexadecimal flag in the the `Debug` trait is superficially redundant +with the `LowerHex` and `UpperHex` traits. +If these traits were not stable yet, we could have considered a more unified design. + +# Rationale and alternatives +[alternatives]: #alternatives + +Implementing `LowerHex` and `UpperHex` was proposed and rejected +in [PR #44751](https://github.com/rust-lang/rust/pull/44751). + +The status quo is that debugging or testing code that could be a one-liner +requires manual `Debug` impls and/or concatenating the results of separate +string formatting operations. + +# Unresolved questions +[unresolved]: #unresolved-questions + +* Should this be extended to octal and binary (as `{:o?}` and `{:b?}`)? + Other formatting types/traits too? +* Details of the new `Formatter` API From 23ca0a5d1a2eeb5bf29a30ff52419de60694d350 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 21 Jan 2018 10:39:37 +0100 Subject: [PATCH 2/4] Typo fix --- text/0000-fmt-debug-hex.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-fmt-debug-hex.md b/text/0000-fmt-debug-hex.md index 032d9d44b28..752a17ec5f6 100644 --- a/text/0000-fmt-debug-hex.md +++ b/text/0000-fmt-debug-hex.md @@ -7,7 +7,7 @@ [summary]: #summary Add support for formatting integers as hexadecimal with the `fmt::Debug` trait, -including when the occur within larger types. +including when they occur within larger types. ```rust println!("{:02X?}", b"AZaz\0") From 498198684c29d628b1295230471246e33bcb516d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 29 Jan 2018 10:21:53 +0100 Subject: [PATCH 3/4] Typo fix --- text/0000-fmt-debug-hex.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-fmt-debug-hex.md b/text/0000-fmt-debug-hex.md index 752a17ec5f6..100d46107ba 100644 --- a/text/0000-fmt-debug-hex.md +++ b/text/0000-fmt-debug-hex.md @@ -23,7 +23,7 @@ Sometimes the bits that make up an integer are more meaningful than its purely n For example, an RGBA color encoded in `u32` with 8 bits per channel is easier to understand when shown as `00CC44FF` than `13387007`. -The `std::fmt::UpperHex` and `std::fmt::LowerHex` provide hexadecimal formatting +The `std::fmt::UpperHex` and `std::fmt::LowerHex` traits provide hexadecimal formatting through `{:X}` and `{:x}` in formatting strings, but they’re only implemented for plain integer types and not other types like slices that might contain integers. From 3f3b3a3eec536f05cc03be4f611f87a0122caa19 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 27 Feb 2018 06:28:48 -0800 Subject: [PATCH 4/4] RFC 2226 --- text/{0000-fmt-debug-hex.md => 2226-fmt-debug-hex.rs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-fmt-debug-hex.md => 2226-fmt-debug-hex.rs} (97%) diff --git a/text/0000-fmt-debug-hex.md b/text/2226-fmt-debug-hex.rs similarity index 97% rename from text/0000-fmt-debug-hex.md rename to text/2226-fmt-debug-hex.rs index 100d46107ba..fd0b02bfd43 100644 --- a/text/0000-fmt-debug-hex.md +++ b/text/2226-fmt-debug-hex.rs @@ -1,7 +1,7 @@ - Feature Name: fmt-debug-hex - Start Date: 2017-11-24 -- RFC PR: -- Rust Issue: +- RFC PR: https://github.com/rust-lang/rfcs/pull/2226 +- Rust Issue: https://github.com/rust-lang/rust/issues/48584 # Summary [summary]: #summary