Reusing an fmt::Formatter


#1

I’m working on a template engine. As such, I’d like to be able to create an API where I accept a Display, and then write the resulting output into an output buffer. From looking at the documentation, it seems as if there is no stable way to create a Formatter, so in order to write the Display value for multiple things into a single buffer, I’d have to use the formatting macros to create Strings for each thing, then write them into a Write separately.

It appears like I might be able to do something smarter if I’d enable nightly’s fmt_internals, but even then it seems to be pretty ugly to set up a longer-lived Formatter.

Is this understanding correct, or are there other ways of doing what I want?


#3

There’s the write! macro, for example

use std::fmt;

fn foo(d: &fmt::Display, mut i: &mut fmt::Write) -> fmt::Result {
    write!(i, "test {}\t", d)
}

fn main() {
    let mut buf = String::new();
    foo(&5, &mut buf).unwrap();
    foo(&"this", &mut buf).unwrap();
    println!("{}", buf)
}

#4

In addition to using write! and format_args!, you can use various wrapper types to make it easier to work with the formatter (through the format traits). One of the most versatile ones is a combinator (Fmt below) that simply gives you access to the formatter in a closure:

use std::fmt;

pub struct Fmt<F>(pub F) where F: Fn(&mut fmt::Formatter) -> fmt::Result;

impl<F> fmt::Debug for Fmt<F>
    where F: Fn(&mut fmt::Formatter) -> fmt::Result
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        (self.0)(f)
    }
}

struct Mock {
    data: [i32; 40],
}

impl fmt::Debug for Mock {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Mock")
         .field("data", &Fmt(|f| f.debug_list().entries(self.data.iter()).finish()))
         .finish()
    }
}

fn main() {
    println!("{:#?}", Mock { data: [3; 40] } );
}

The full version of the Fmt formatter combinator would implement all the formatting traits, not just one of them.