Feature request: Add a way to capture a formatter that can be passed to format helper functions · Issue #74870 · rust-lang/rust (original) (raw)

Motivation

The Display trait allows lazy formatting of data. The formatter is passed in and used by the implemented fmt function for the struct. Often however applications and libraries want to expose helper functions to compose the display parts of a complex struct.

There are several ways to solve that:

  1. Return helper structs from each of those helper functions and implement display for each of them
  2. Use a buffer to write the data and pass in a Vec! (but loose all formatting parameters that way)
  3. Use internal functions within the fmt() function itself
  4. is not always feasible, 2) does use a wasteful extra buffer (which fmt avoids), 3) is possible when the helper functions should be private.

The motivation however here is that not only the Display::fmt function can be called by the user, but also the helper functions to format just parts of the struct (in our use-case different statistics).

The signature of the helper functions is the same as of fmt in the Display trait:

pub fn fmt_xyz(&self, fmt: &mut fmt::Formatter) -> fmt::Result { writeln!(fmt, '{}', self.field_1); }

But it is not possible to construct a formatter manually.

The only way to be able to call the helper function is to use:

The format crate or a helper Fmt() struct, which can capture the formatter passed in.

Proposed resolution

To solve that I am arguing that either a macro or a Fmt helper trait should be available in core rust so that there exists no function that you just can't call as a user (without an extra crate or helper trait).

The usage looks for example like this:

format!("{}", Fmt(|fmt| my_struct.fmt_xyz(fmt)));

The implementation of Fmt is as follows:

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

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

(from: https://users.rust-lang.org/t/reusing-an-fmt-formatter/8531/3)

Discussion

I am not sure if Fmt is the right name as it feels a little bit weird.

Alternative proposals are like the format crate does it using a macro:

lazy_format!("{}", |fmt| my_struct.fmt_xyz(fmt));

Or what would be great if via some compiler magic a closure could directly work:

format!("{}", |fmt| my_struct.fmt_xyz(fmt));

From a user experience perspective the last version feels ideal, but I am not sure how difficult it would be to implement that.

Next steps

Change scope

The scope is a small addition and hence seemed to not need a RFC per the guide lines.


If this has a chance of going in, I am happy to provide a PR.