Why can't we create a std::fmt::Formatter?


#1

I am curious if anyone knows the reason why we can’t create a std::fmt::Formatter? It forces us to use the write! macro (or similar), which then prevents us from seamlessly mixing use of the Display, Debug, Hex, etc traits with any similar traits we might design. This doesn’t seem very good for extensibility. Any ideas why this is so?


#2

To be honest I’ve never found myself wishing for this functionality (and I’m not sure what the formatter would be constructed from… I guess from a Write implementor?). Can you give an example of what you’d be able to do that isn’t possible with the macros?


#3

Yes, I’d want to create a formatter from something that implements Write. e.g. if I wanted to implement a variant of write! myself.

The use case here is for my display-as crate that implements alternative formatting options (and also a compile-time template system). The standard library already has nine such traits, so it seems logical to support having more so they can all be used interchangeably. However, making use of any of these traits requires using macros, which can be inconvenient.


#4

But it’s impossible to be generic over a trait, so the macros seem necessary to me? (also, it looks like your crate has plenty of other reasons for using macros, what with that rusty DSL thing it has going on)

If it’s that you can’t just use one of the traits without calling a macro, you certainly can; you just write a function that wraps the invocation of write!.

I guess I should rephrase my question: Is there a specific function signature you’d like to provide, or a specific example of user code you’d like to support that you currently cannot?


#5

I’ve wanted this for implementing a runtime formatting library. Not every option that can be set inside a Formatter can be set from a format string using runtime values (for example, I don’t believe you can toggle the “alternative representation” flag by a value). This means you need to hard-code every possible combination of formatting flags, and dispatch to those through the formatting system and… eurgh.

In the end, I gave up and just half-assed the library. All I needed to full-ass it was the ability to construct a Formatter directly. It’s profoundly frustrating to know the interface you need is there, but you’re just not allowed to use it.


#6

The immediate trigger of this question was the desire to create a version of the format! macro that works with my own Display-style traits. This requires me to create a mut String and then apply my functions to that string, but while the mut String is Write, it is not a formatter. As you say, I could wrap every function call in write!(..., "{}", value), but that requires me to implement Display (or pick another of the nine) on every type that is printed. Yes, I can do that, but it requires always using a wrapper type that implements Display. The pressure against this is that I want Deref coercion to work for my macro arguments, which is far easier to manage if I could call my methods directly (which I could, if I could just get a Formatter out of my string. As it turns out, I managed to get Deref coercion to work by creating a throwaway trait for every invocation and wrapping the type in a wrapper that implements Display, so I think I’ve worked around the inability to get a Formatter. But it wasn’t either pretty or easy. Were I able to get a Formatter, I could just call my own fmt method as a method call, which would naturally enable Deref coercion.


#7

Okay, I see the issue with Deref coercions.

Mind, my reason for demanding a precise signature or an ideal user code sample isn’t just to be antagonistic. Honestly, I don’t think it’s unreasonable to expose a constructor for Formatter. I just want to help make sure that this change is enough. All too often, I’ve tried adding a feature to some library that I thought I needed to achieve some use case, only to discover that there was some greater issue I didn’t even anticipate.

Having a concrete sample to talk about can help others find these unanticipated issues that may get in the way of your ideal API.