Skip to content

Commit 16cfef8

Browse files
authored
Merge pull request #2226 from SimonSapin/fmt-debug-hex
Hexadecimal integers with fmt::Debug, including within larger types
2 parents 27f4519 + 3f3b3a3 commit 16cfef8

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

text/2226-fmt-debug-hex.rs

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
- Feature Name: fmt-debug-hex
2+
- Start Date: 2017-11-24
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/2226
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/48584
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add support for formatting integers as hexadecimal with the `fmt::Debug` trait,
10+
including when they occur within larger types.
11+
12+
```rust
13+
println!("{:02X?}", b"AZaz\0")
14+
```
15+
```
16+
[41, 5A, 61, 7A, 00]
17+
```
18+
19+
# Motivation
20+
[motivation]: #motivation
21+
22+
Sometimes the bits that make up an integer are more meaningful than its purely numerical value.
23+
For example, an RGBA color encoded in `u32` with 8 bits per channel is easier to understand
24+
when shown as `00CC44FF` than `13387007`.
25+
26+
The `std::fmt::UpperHex` and `std::fmt::LowerHex` traits provide hexadecimal formatting
27+
through `{:X}` and `{:x}` in formatting strings,
28+
but they’re only implemented for plain integer types
29+
and not other types like slices that might contain integers.
30+
31+
The `std::fmt::Debug` trait (used with `{:?}`) however is intended for
32+
formatting “in a programmer-facing, debugging context”.
33+
It can be derived, and doing so is recommended for most types.
34+
35+
This RFC proposes adding the missing combination of:
36+
37+
* Output intended primarily for end-users (`Display`) v.s. for programmers (`Debug`)
38+
* Numbers shown in decimal v.s. hexadecimal
39+
40+
# Guide-level explanation
41+
[guide-level-explanation]: #guide-level-explanation
42+
43+
In formatting strings like in the `format!` and `println!` macros,
44+
the formatting parameters `x` or `X` − to select lower-case or upper-case hexadecimal −
45+
can now be combined with `?` which select the `Debug` trait.
46+
47+
For example, `format!("{:X?}", [65280].first())` returns `Some(FF00)`.
48+
49+
This can also be combined with other formatting parameters.
50+
For example, `format!("{:02X?}", b"AZaz\0")` zero-pads each byte to two hexadecimal digits
51+
and return `[41, 5A, 61, 7A, 00]`.
52+
53+
An API returning `Vec<u32>` might be tested like this:
54+
55+
```rust
56+
let return_value = foo(bar);
57+
let expected = &[ /* ... */ ][..];
58+
assert!(return_value == expected, "{:08X?} != {:08X?}", return_value, expected);
59+
```
60+
61+
# Reference-level explanation
62+
[reference-level-explanation]: #reference-level-explanation
63+
64+
## Formatting strings
65+
66+
The syntax of formatting strings
67+
is [specified with a grammar](https://doc.rust-lang.org/std/fmt/#syntax)
68+
which at the moment is as follows:
69+
70+
```
71+
format_string := <text> [ maybe-format <text> ] *
72+
maybe-format := '{' '{' | '}' '}' | <format>
73+
format := '{' [ argument ] [ ':' format_spec ] '}'
74+
argument := integer | identifier
75+
76+
format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][type]
77+
fill := character
78+
align := '<' | '^' | '>'
79+
sign := '+' | '-'
80+
width := count
81+
precision := count | '*'
82+
type := identifier | ''
83+
count := parameter | integer
84+
parameter := argument '$'
85+
```
86+
87+
This RFC adds an optional *radix* immediately before *type*:
88+
89+
```
90+
format_spec := [[fill]align][sign]['#']['0'][width]['.' precision][radix][type]
91+
radix: 'x' | 'X'
92+
```
93+
94+
## `Formatter` API
95+
96+
Note that `x` and `X` are already valid *types*.
97+
They are only interpreted as a radix when the type is `?`,
98+
since combining them with other types doesn’t make sense.
99+
100+
This radix is exposed indirectly in two additional methods of `std::fmt::Formatter`:
101+
102+
```rust
103+
impl<'a> Formatter<'a> {
104+
// ...
105+
106+
/// Based on the radix and type: 16, 10, 8, or 2.
107+
///
108+
/// This is mostly useful in `Debug` impls,
109+
/// where the trait itself doesn’t imply a radix.
110+
fn number_radix(&self) -> u32
111+
112+
/// true for `X` or `E`
113+
///
114+
/// This is mostly useful in `Debug` impls,
115+
/// where the trait itself doesn’t imply a case.
116+
fn number_uppercase(&self) -> bool
117+
}
118+
```
119+
120+
Although the radix and type are separate in the formatting string grammar,
121+
they are intentionally conflated in this new API.
122+
123+
## `Debug` impls
124+
125+
The `Debug` implementation for primitive integer types `{u,i}{8,16,32,64,128,size}`
126+
is modified to defer to `LowerHex` or `UpperHex` instead of `Display`,
127+
based on `formatter.number_radix()` and `formatter.number_uppercase()`.
128+
The *alternate* `#` flag is ignored, since it already has a separate meaning for `Debug`:
129+
the `0x` prefix is *not* included.
130+
131+
As of Rust 1.22, impls using the `Formatter::debug_*` methods do not forward
132+
formatting parameters such as *width* when formatting keys/values/items.
133+
Doing so is important for this RFC to be useful.
134+
This is fixed by [PR #46233](https://github.com/rust-lang/rust/pull/46233).
135+
136+
# Drawbacks
137+
[drawbacks]: #drawbacks
138+
139+
The hexadecimal flag in the the `Debug` trait is superficially redundant
140+
with the `LowerHex` and `UpperHex` traits.
141+
If these traits were not stable yet, we could have considered a more unified design.
142+
143+
# Rationale and alternatives
144+
[alternatives]: #alternatives
145+
146+
Implementing `LowerHex` and `UpperHex` was proposed and rejected
147+
in [PR #44751](https://github.com/rust-lang/rust/pull/44751).
148+
149+
The status quo is that debugging or testing code that could be a one-liner
150+
requires manual `Debug` impls and/or concatenating the results of separate
151+
string formatting operations.
152+
153+
# Unresolved questions
154+
[unresolved]: #unresolved-questions
155+
156+
* Should this be extended to octal and binary (as `{:o?}` and `{:b?}`)?
157+
Other formatting types/traits too?
158+
* Details of the new `Formatter` API

0 commit comments

Comments
 (0)