-
Notifications
You must be signed in to change notification settings - Fork 20
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
Figure out what to do with {:x?} #165
Comments
We discussed this in a recent libs-api meeting. The summary of the discussion is that we think We suspect that the majority of use cases of
For 1, For 2, This isn't to say that we guarantee to accept a proposal for either of these things—any such proposal will still have to go through the usual process—but these do sound like reasonable things to have. (If we end up having better solutions for the vast majority of use cases of |
One other possibility worth exploring would be to have methods on slices and clonable iterators which provide |
Related: rust-lang/rust#75766. |
I've been giving this a lot of thought over the last few days. At first, I really liked @dbdr's idea:
But the breaking change is unfortunate, and it doesn't compose very well with padding and width. The "nested format" syntax suggested by @m-ou-se is okay, but a little ugly. One alternative that combines the two approaches is putting the element formatting after the
But going through these options just raises more questions for me:
APIThis depends on the answers to the questions above, but I'll still share my thought process so far. Originally, I thought of using a new trait for lists with an associated trait ListDebug {
type Item;
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
impl<T> ListDebug for [T] {
type Item = T;
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self).finish()
}
}
impl<T: ListDebug> Debug for T { /* delegate fmt */ } But I quickly realized it would be difficult or impossible to make trait Debug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
/// `?x` formatting
///
/// Formats the value using the given formatter,
/// formatting entries with `LowerHex` when possible.
///
/// By default, this just calls `Debug::fmt`. Types
/// implementing both `Debug` and `LowerHex` should
/// override this function to call `LowerHex::fmt`.
///
/// This gets used by the `entry` and `entries`
/// functions of `DebugList`, `DebugMap`, and `DebugSet`
/// when `f.debug_entry_flags().lower_hex() == true`.
///
/// The `Formatter` passed to this function is constructed
/// from the format specifier after the `?`.
fn fmt_entry_lower_hex(&self, f: &mut fmt::Formatter<'_>) {
Debug::fmt(&self, f);
}
// ... and so on
} But what is stated about What I ended up at was the following: #[derive(Clone, Copy)]
#[non_exhaustive]
pub enum DebugNumberMode {
LowerHex,
UpperHex,
Octal,
Binary,
LowerExp,
UpperExp,
None,
}
pub struct DebugNumber<'a>(fmt::Formatter<'a>);
impl<'a> DebugNumber<'a> {
fn display(&mut self, n: &dyn fmt::Display) -> fmt::Result {
n.fmt(&mut self.0)
}
fn lower_hex(&mut self, n: &dyn fmt::LowerHex) -> fmt::Result {
n.fmt(&mut self.0)
}
// ...
}
impl<'a> Formatter<'a> {
pub fn debug_number(&mut self) -> (DebugNumberMode, DebugNumber<'a>) {
// return the entry formatting mode and new formatter based on the entry formatting flags
}
}
// Usage
impl fmt::Debug for MyInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (mode, mut f) = debug_number(f);
match mode {
DebugNumberMode::LowerHex => f.lower_hex(self),
// ...
_ => f.display(self),
}
}
} One variation on this is to require that someone who wants to use this API implement all of the traits and then only offer the following: impl Formatter<'_> {
pub fn debug_number(&mut self, n: &dyn Debug + Display + LowerHex + UpperHex ...) -> Result {
// match on entry formatting mode
// call the correct trait with a new formatter based on the entry formatting options
}
} If we want to support more than just numeric types, we could also provide impl Formatter<'_> {
pub fn debug_string(&mut self, s: &dyn Debug) -> Result {
// call `Debug::fmt` with a new formatter based on the entry formatting options
}
} |
I personally have made traits similar to the pub trait DebugIter<'a> {
type Iter: 'a + Iterator<Item = &'a dyn Debug>;
fn debug_iter(&'a self) -> Self::Iter;
} Since any such iterator can easily be composed into a final Of course, the whole iterator construction isn't super necessary in most cases. I wonder if the right approach is really to just have methods on |
See
The text was updated successfully, but these errors were encountered: