Rust 直接调用 fmt 函数

Rust calling fmt function directly

我正在尝试根据参数实现不同的 Display 格式。就像是 print_json()print_pretty()。我当然可以将它实现为返回字符串 print_json(&self)->String 的函数,但我想知道是否可以使用 print_json(&self,f: &mut Formatter<'_>) -> std::fmt::Resultprint_pretty(&self,f: &mut Formatter<'_>) -> std::fmt::Result 来代替。然后我可以根据用例调用这些函数中的任何一个。但是如何直接获取 Formatter 的实例呢?理想情况下,我想做类似

的事情
let mut string = String::new();
my_object.print_pretty(&mut string);
return string;

But how do I obtain instance of Formatter directly?

据我所知,获得 Formatter 的唯一方法是通过实施 Display 或其他格式特征之一来接收一个。

我已经根据这个核心特征实施了自己的方案来添加更多格式选项:

/// Objects for which alternate textual representations can be generated.
/// These are analogous to [`Display`] and [`Debug`], but have additional options.
pub trait CustomFormat<F: Copy> {
    /// Wrap this value so that when formatted with [`Debug`] or [`Display`] it uses
    /// the given custom format instead.
    fn custom_format(&self, format_type: F) -> CustomFormatWrapper<'_, F, Self> {
        CustomFormatWrapper(format_type, self)
    }

    /// Implement this to provide custom formatting for this type.
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, format_type: F) -> fmt::Result;
}

希望支持其他格式的类型实现 CustomFormat<F> 其中 F 是特定的格式类型或格式选项;对于您的用例,它们可能是 struct Json;struct Pretty;。或者,您可以有两个不同的特征 — 我刚刚发现拥有一个通用的特征可以减少代码重复。

作为一个实现示例,这里我为std::time::Duration定义了一个自定义格式。它看起来就像 DebugDisplay 实现,除了它需要一个额外的 format-options 参数(它忽略了因为 StatusText 没有携带任何额外的选项):

impl CustomFormat<StatusText> for Duration {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, _: StatusText) -> fmt::Result {
        write!(fmt, "{:5.2?} ms", (self.as_micros() as f32) / 1000.0)
    }
}

您使用这些工具的代码示例为:

let mut string = String::new();
write!(string, "{}", my_object.custom_format(Pretty)).unwrap();
return string;

"{}" 格式字符串不再控制格式;它只是一个调用格式机制的占位符,通过实现 Display(以及 Debug)的 CustomFormatWrapper 来调用自定义格式:

pub struct CustomFormatWrapper<'a, F: Copy, T: CustomFormat<F> + ?Sized>(F, &'a T);

impl<'a, F: Copy, T: CustomFormat<F>> Debug for CustomFormatWrapper<'a, F, T> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        <T as CustomFormat<F>>::fmt(self.1, fmt, self.0)
    }
}

impl<'a, F: Copy, T: CustomFormat<F>> Display for CustomFormatWrapper<'a, F, T> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        <T as CustomFormat<F>>::fmt(self.1, fmt, self.0)
    }
}

对于您的目的而言,这可能是一个过度设计的解决方案。所需的关键元素是包装器类型,它包含对要格式化的值的引用,并且它 一些 标准格式特征,例如 Display 转发给您的自定义格式化特征(或者,如果您只想自定义格式化一种类型,只需自定义该对象上的方法)。

Link to full source code in context