Currently format!
supports combining hex formatting (x
) and debug formatting (?
). This allows one to get hexadecimal numbers in the debug output.
However, this is currently very limited:
- Uses an internal-only API not available to third-party types
- Debug alt-mode affects formatting at all levels: you can't use
#
to get the0x
in front of hexadecimals without also triggering "alt mode" (usually pretty-printed) debug output for everything else
These limitations have caused the library team to pause adding more number formatting options in Debug
output.
Sketch
Essentially, the idea implemented in three parts:
- Change the syntax so
#
only applies to only the subsequent character, rather than the whole formatter.
{:x?}
behavior does not change- use
{:#x#?}
to get the same behavior as the current{:#x?}
- use
{:#x?}
to get0x
prefix without pretty-printing - use
{:x#?}
to get pretty-printing without0x
prefix
- Extend the syntax to support octal, binary, etc
- Provide a
.debug_list()
-like helper for delegating toLowerHex
, etc
pub struct DebugNumber<'b, 'c, T: ?Sized> {
/// Number being formatted
number: &'c T,
/// New formatter built from settings after the `?`
formatter: fmt::Formatter<'b>,
/// Number formatting mode
mode: DebugNumberMode,
/// Function to use for `?`
default: fn(&T, &mut fmt::Formatter<'b>) -> fmt::Result,
/// Function to use for `?x`
lower_hex: fn(&T, &mut fmt::Formatter<'b>) -> fmt::Result,
/// Function to use for `?b`
binary: fn(&T, &mut fmt::Formatter<'b>) -> fmt::Result,
// ....
}
impl<'a> Formatter<'a> {
/// Creates a `DebugNumber` builder designed to assist
/// with creation of `fmt::Debug` implementations for
/// numeric values.
///
/// Used to enable alternate number formatting flags
/// in combination with `Debug`, like in `{:?x}`.
///
/// # Examples
///
/// ```
/// impl fmt::Debug for u32 {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt
/// .debug_number(self, fmt::Display::fmt)
/// .lower_hex()
/// .binary()
/// .finish()
/// }
/// }
/// ```
pub fn debug_number<T: fmt::Debug>(number: &'c T, default: fn(&T, &mut fmt::Formatter<'b>) -> fmt::Result) -> Self {
DebugNumber {
number,
formatter,
mode,
default,
lower_hex: default,
binary: default,
}
}
}
Which has a function to enable each
impl<'b, 'c, T: fmt::Debug + ?Sized> DebugNumber<'b, 'c, T> {
/// Enable `?x` formatting for this number.
pub fn lower_hex(&mut self) -> &mut Self
where
T: fmt::LowerHex
{
self.lower_hex = |number, formatter| {
fmt::LowerHex::fmt(number, formatter)
};
self
}
/// Enable `?b` formatting for this number.
pub fn binary(&mut self) -> &mut Self
where
T: fmt::Binary
{
self.binary = |number, formatter| {
fmt::Binary::fmt(number, formatter)
};
self
}
// ....
/// Complete the formatting, delegating to whichever
/// numeric formatting was specified after the `?`.
pub fn finish(&mut self) -> fmt::Result {
match self.mode {
DebugNumberMode::LowerHex => (self.lower_hex)(self.number, &mut self.formatter),
DebugNumberMode::Binary => (self.binary)(self.number, &mut self.formatter),
// ....
_ => (self.default)(self.number, &mut self.formatter),
}
}
}
API proof-of-concept playground