I can think of many more options: order the terms from highest to lowest exponent, don't show T^0, print T^1 as T, etc.
My question is basically just if Formtters are suitable to implement this kind of formatting options, or if it is more appropriate to write a method like
I've used the alternate flag when there was only a couple different Display possibilities, but it felt a bit dirty. I've considered implementing the other fmt traits too, but that feels even worse.
Instead I usually end up using some sort of newtype pattern:
// Doesn't have to be generic on the type, but...
pub struct NonZeroPoly<T>(pub T);
// ...sometimes this can be generic on `AsRef` or such so that's my habit
impl fmt::Display for NonZeroPoly<&Polynomial> {
// ...
}
fn elsewhere(poly: Polynomial) {
println!("{}", NonZeroPoly(&poly));
}
The standard library has Path::display() which displays a potentially lossy form of the path, that's possibly a good start for whatever design you end up with.
The main distinction between using a Display/Debug implementation and just returning a string is that the caller can provide some common formatting options like "alternate", or "hex" and you save the extra String allocation.
I'd use the formatter options only for behaviours that are similar to other displayed objects, like width for the width in the string and precision for the precision of the numbers in your string. Creative use of those options or of the other traits (LowerHex, Octal, ...) will only confuse the users of your crate, I think.
For other difference of formats like those you gave in example, I'd return an object that implements Display, in which you can put a few options if necessary, or use a wrapper (the new type pattern) as advised above, without overdoing it.
I've used the alternate flag when there was only a couple different Display possibilities, but it felt a bit dirty.
but if I understand correctly, I could use this to implment exactly 2 different Display possibilites, rigt?
And about this:
At first I was a bit confused because I thought the NonZeroPoly type was supposed to hold only non-zero polynomials. But if I understand correctly, the idea was to implement a fmt method for the NonZeroPoly type which prints polynomials ignoring the zero coefficients, right? So if I wanted to have 4 different ways of formatting polynomials, I would implement 4 structs like this?
You could have multiple wrappers and/or fine-tune some of them with options, like a NonZeroPoly { poly: &Polynomial, show_1_coefficients: bool }. It's more awkward to instantiate, but it can be done with a method in Polynomial.
The alternate option is when you add the # flag, like format!("{p:#}"), or in Debug with "{p:#?}", which normally pretty-prints the data. You can query the flag in your Display (or Debug) implementation to vary the format, yes.
I've always wished Rust had taken a more extensible approach to formatting; something more like trait Format<F: Style>. I wonder if it would be theoretically possible to shoehorn in after the fact (though I know it won't be).
I wrote a library to provide the closest approach to extensible formatting parameters/traits that is possible:
It works like Path::display(), requiring the user to call a method to add a wrapper, but it is designed so that adding/implementing formatting doesn't require you to write your own wrapper or trait; only write one impl. That implementation can then take a struct with arbitrary additional options. Formatting can be added for types from third-party crates, too.
Not related to the question asked, but from the code snippet: instead of accumulating in a string via format! and then write!-ing that out at the end, you can just as easily use write! at each step.
That's funny, I wrote my own (largely toy) implementation of a similar concept, and originally called it refmt. However I was aiming for a theoretical replacement for std::fmt, rather than integrating with it; your approach is very interesting!