New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hexadecimal integers with fmt::Debug, including within larger types #2226
Merged
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks. |
||
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<u32>` 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 := <text> [ maybe-format <text> ] * | ||
maybe-format := '{' '{' | '}' '}' | <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 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
s/the/they