我有一个结构体,它可以通过一系列包含大量write!调用的复杂方法调用转换为文本。此文本可以写入文件或调试日志。我正在尝试决定是否使用fmt::Write或io::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)
正确的做法是Display为您的类型实现,而不是write_it根本没有方法。理想情况下,您的header、content,并且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,但它本身不会创建任何中间字符串。