Easy way to get fmt::Formatter?


#1

Hello, in nl-utils (which currently just dumps Netlink Route packets from a pcap file) I have a lot of code that is written similar to this:

use std::fmt;

fn format_indent(indent: i32) -> String {
    let mut s = String::new();
    for _ in 0..indent {
        s.push_str("    ");
    }
    s
}

#[derive(Default)]
struct Type {
    val: i32,
}
impl Type {
    pub fn pretty_fmt(&self, f: &mut fmt::Formatter, indent: i32) -> fmt::Result {
        let i_s = format_indent(indent);
        try!(write!(f, "{{\n"));
        try!(write!(f, "{}    val: {}\n", i_s, self.val));
        write!(f, "{}}}", i_s)
    }
}
impl fmt::Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.pretty_fmt(f, 0)
    }
}

#[derive(Default)]
struct CompositeType {
    x: Type,
}
impl CompositeType {
    pub fn pretty_fmt(&self, f: &mut fmt::Formatter, indent: i32) -> fmt::Result {
        let i_s = format_indent(indent);
        try!(write!(f, "{{\n"));
        try!(write!(f, "{}    x: ", i_s));
        try!(self.x.pretty_fmt(f, indent + 1));
        write!(f, "\n{}}}", i_s)
    }
}
impl fmt::Display for CompositeType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.pretty_fmt(f, 0)
    }
}

fn main() {
    let v = CompositeType::default();
    println!("v = {}", v);
}

This lets any of my structs be formatted with print!(), println!(), format!() and the like while also accounting for the indentation of nested structs. Now, if I wanted to call v.pretty_fmt() in main(), how could I easily get a mutable reference to the fmt::Formatter for stdout?


#2

You don’t.

So far as I can tell, there is exactly one function in the stdlib that can create a Formatter, and that’s fmt::write. Arguments can only be stably constructed using format_args!, which you pass a format string to… at which point, you might as well just use print! or println!.


#3

As someone that is pretty new to the Rust community that seems like a real deficiency. I have managed to work around it with a trait for pretty_fmt() and a generic struct to call pretty_fmt().

trait IsPretty {
    fn pretty_fmt(&self, f: &mut fmt::Formatter, indent: i32) -> fmt::Result;
}

struct Indent<T> {
    t: T,
    i: i32,
}
impl<T> fmt::Display for Indent<T> where T: IsPretty{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.t.pretty_fmt(f, self.i)
    }
}