对我的可写类型使用 fmt::Write 或 io::Write trait?

cam*_*ous 6 rust

我有一个结构体,它可以通过一系列包含大量write!调用的复杂方法调用转换为文本。此文本可以写入文件或调试日志。我正在尝试决定是否使用fmt::Writeio::Write。我不能真正使用两者,因为那样所有的写作方法都需要复制。

简化示例:

impl MyType {
    fn write_it(&self, writer: &mut impl ???) {
//                                       ^ fmt::Write or io::Write?
        self.write_header(writer);
        self.write_contents(writer);
        self.write_footer(writer);
    }

    fn write_header(&self, writer: &mut impl ???) {
        write!(writer, "...")
    }
    // and more...
}
Run Code Online (Sandbox Code Playgroud)

文档fmt::Write说,

...这类似于标准库的 io::Write 特性,但它仅用于 libcore。

所以这让我相信我应该使用io::Write. 这(显然)对于 I/O 类型(如BufWriter. 似乎值得注意的是serde_json 是这样做的

// if I use io::Write, I can do this
my_type.write_it(&mut BufWriter::new(File::create("my-file.txt")?)?;
Run Code Online (Sandbox Code Playgroud)

我还想将我的类型与format!类似的宏一起使用。所以我需要实施Display. 事实上,不是Display可以表示为 a 的类型的事实上的特征String吗?

// I want to do this
println!("Contents:\n{}", my_type);

// or this
let mut s = String::new();
write!(s, "{}", my_type);
Run Code Online (Sandbox Code Playgroud)

所以我想我只会将我的实现与Display. 但问题来了:

impl Display for MyType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.write_it(f)?;
//                    ^ the trait `std::io::Write` is not implemented for `std::fmt::Formatter<'_>`
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以通过使用来解决这个问题impl fmt::Write,但是我失去了之前提到的 I/O 功能。这两种类型之间没有适配器(如果这是预期用途,我认为会有一个)。所以我夹在两者之间。我应该使用哪个特征?有没有一种惯用的方法来获得两者的功能?

编辑:适配器?

@ÖmerErden 建议创建一个如下所示的适配器。我只是不确定什么条件会导致 UTF-8 转换失败。只要我用 编写有效的 UTF-8,该转换是否保证不会失败write!?这对我来说似乎太冒险了。

struct Adapter<'a> {
    f: &'a mut dyn fmt::Write,
}

impl<'a> io::Write for Adapter<'a> {
    fn write(&mut self, b: &[u8]) -> Result<usize, io::Error> {
        let s = str::from_utf8(b)
            .map_err(|_| io::Error::from(io::ErrorKind::Other))?;
        self.f.write_str(s)
            .map_err(|_| io::Error::from(io::ErrorKind::Other))?;
        Ok(b.len())
    }

    fn flush(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
}
Run Code Online (Sandbox Code Playgroud)

Luc*_*iel 6

正确的做法是Display为您的类型实现,而不是write_it根本没有方法。理想情况下,您的headercontent,并且footer还实现Display,因为这样您就可以编写如下内容:

impl Display for MyType {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}{}{}", self.header, self.content, self.footer)
    }    
}
Run Code Online (Sandbox Code Playgroud)

但是,如果不可能,您仍然可以像在示例代码中那样手动编写实现。

事实上, Display 不是可以表示为 String 的类型的事实上的特征吗?

更准确的说法是,这Display是可以写入某些内容的类型的事实上的特征。将事物转换为字符串只是 的​​众多用例之一Display,但它本身不会创建任何中间字符串。