我有一个函数可以将我的DocObj渲染成一个 fmt::Write
:
impl DocObj {
fn render(&self, write: &mut dyn Write) -> io::Result<()>;
}
执行 fmt::Display
我想把同样的字节写到 fmt::Formatter
不需要复制和粘贴代码。
我是一个锈迹斑斑的新手,我试过各种解决方案,但至今没有任何效果。
下面是我试过的方法。
策略一: Write
特质 std::Formatter
失败的原因是锈迹不让我在另一个箱子里实现一个类型的特征。 Fails because rust won't let me implement a trait for a type in another crate. 好吧。
策略 2: 为以下类型创建一个小包装器 std::Formatter
实现 Write
失败的原因是我对寿命和锈的理解还不够深刻。
struct DisplayWriter<'a> {
formatter: &'a fmt::Formatter<'a>
}
impl<'a> DisplayWriter<'a> {
fn from_formatter(aformatter: &'a mut fmt::Formatter<'a>) -> DisplayWriter<'a> {
DisplayWriter {formatter: aformatter}
}
}
impl<'a> io::Write for DisplayWriter<'a> {
fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, std::io::Error> {
use std::fmt::Write;
for c in bytes.iter() {
match self.formatter.write_char(*c as char) {
Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err))
}
}
Ok(bytes.len())
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> { todo!() }
}
impl fmt::Display for DocObj {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let write = DisplayWriter::from_formatter(f);
match self.render(&mut write) {
Ok(_) => Ok(()),
Err(err) => Err(fmt::Error)
}
}
}
用这段代码,编译器抱怨说有错误信息。
error[E0623]: lifetime mismatch
--> src\cos.rs:286:51
|
285 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
| -------------------
| |
| these two types are declared with different lifetimes...
286 | let write = DisplayWriter::from_formatter(f);
| ^ ...but data from `f` flows into `f` here
关于我的上下文的一些说明:
谢谢你的帮助
std::Formatter
已经实现了 Write
所以你可以通过调用你的 render
方法中直接使用。
impl Display for DocObj {
fn fmt (&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.render (f).or (Err (fmt::Err))
}
}
如果你想在一个结构中封装一个对formatter的引用,你需要对引用和通用参数使用不同的寿命。
struct DisplayWriter<'a, 'b> {
formatter: &'a mut fmt::Formatter<'b>
}
完整的代码,包括对其他错误的修复,一旦你有了两个寿命。
use std::{ fmt, io };
struct DocObj {}
impl DocObj {
fn render(&self, write: &mut dyn io::Write) -> io::Result<()> { Ok (()) }
}
struct DisplayWriter<'a, 'b> {
formatter: &'a mut fmt::Formatter<'b>
}
impl<'a, 'b> DisplayWriter<'a, 'b> {
fn from_formatter(aformatter: &'a mut fmt::Formatter<'b>) -> Self {
DisplayWriter {formatter: aformatter}
}
}
impl<'a, 'b> io::Write for DisplayWriter<'a, 'b> {
fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, std::io::Error> {
use std::fmt::Write;
for c in bytes.iter() {
if let Err (err) = self.formatter.write_char(*c as char) {
return Err(io::Error::new(io::ErrorKind::Other, err));
}
}
Ok(bytes.len())
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> { todo!() }
}
impl fmt::Display for DocObj {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut write = DisplayWriter::from_formatter(f);
match self.render(&mut write) {
Ok(_) => Ok(()),
Err(_) => Err(fmt::Error)
}
}
}
在外来类型上实现外来特征的一种方法是使用 新类型 模式。这个模式包括定义一个只包含一个元素的 "元组结构"(在本例中,是外来类型)。
它基本上与你试图实现的包装器相同(@Jmb为你完成的),但少了一些模板。
struct DisplayWriter<'a, 'b>(&'a mut fmt::Formatter<'b>);
impl<'a, 'b> io::Write for DisplayWriter<'a, 'b> {
fn write(&mut self, bytes: &[u8]) -> std::result::Result<usize, std::io::Error> {
bytes.iter()
.try_for_each(|c| self.0.write_char(*c as char) )
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
Ok(bytes.len())
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> { todo!() }
}
我还简化了 write
实现使用函数式。
那么你可以这样使用。
impl fmt::Display for DocObj {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.render(&mut DisplayWriter(f)) {
Ok(_) => Ok(()),
Err(_) => Err(fmt::Error)
}
}
}