If you use:
println!("10 is 0x{:02x} in hex", 10); // ==> "0x0a"
Instead of:
println!("10 is {:#02x} in hex", 10); // "0xa"
It works.
However, as the commenters pointed out this is no bug, but confusion about the meaning of 02
. It does not mean the minimal number of significant digits, but the minimum total number of characters, which now must include 0x
. So this works also:
println!("10 is {:#04x} in hex", 10); // "0x0a"
But there are problems with this solution and the culture that promotes this as the "correct" solution.
First note that the inclusion of 0x
through #
promotes the concept of total width, which the 04
now specifies. One needs to mentally subtract 2 to get to the significant digits, which is the significant part. Regardless {:#04x}
will format 10000 as 0x2710
, which occupies 6 characters, not 4. So the concept of total width fails, as a concept, perhaps too trivially so.
In addition {:#03x}
, {:#02x}
and {:#01x}
confusingly all result in the same behaviour as `{:#x}, and are not flagged by the compiler (1.35) though they make no sense and indicate an error.
Secondly, the #
formatter is only useful for Rust-specific number representations, inserting for example 0x
but not $
(another common hex representation format).
Thirly, crucially, the concept of total width only becomes explicit when the #
formatter is used (and then silently fails in the common byte case). The #
formatter means Rust has the option to consider the total width, and one should use it when it applies (that is in the context of fixed-width layouts).
The remainder shows why #
is a special case solution, and why it is not recommended in the general case.
A formatter string should be chosen depending on the context, specifically how one thinks about displaying a number in that context, as the chosen format expresses intent. So:
{:#04x}
focuses on the total width, useful when doing a rigid fixed-width table layout. The #
formatter clearly is ergonomic in this case. {:#04x}
is also obviously useful when generating a custom formatting string (given a fixed-width context), for example when the output needs to be changed from hex to binary on demand (e.g. {:#04x}
to {:#04b}
), though it is hard to imagine concrete cases.
0x{02x}
expresses the number of significant digits, useful when the main concern is how to represent information on a line, given limited space. A common edit in this case is to remove the 0x
. With 0x{:02x}
the 0x
is simply removed, whereas with the {:#04}
formatter one would need to remove both the #
and substitute the 04
for 02
. The end result is the same in both cases: {:02x}
, but #
required a concept shift.
Depending on the case the #
formatter is ergonomic and expressive of intent, or it is not. Use the format that meshes with what you want to achieve. But in the general case it is probably best to regard #
as a relatively obscure feature. Its additional smartness is only desirable in well-defined cases.
Unfortunately other answers suggest {:#04x}
as the default, "correct" or the "Rust solution". However, the fact a certain feature (#
) is available doesn't mean that:
- Rust endorses it, or it is otherwise recommended to use it,
- the underlying concept of total width needs expressing, or
- the concept of total width should generalize to all of Rust format expressions (which is the root problem most answers demonstrate)
In summary Rust made #
available, but 0x{:02x}
is still often simpler, more to the point and universally recognized. It is KISS and doesn't have hidden defects.