在io::Write中难以封装一个fmt::Formatter。

问题描述 投票:0回答:1

我有一个函数可以将我的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

关于我的上下文的一些说明:

  1. 我想显示的字节是 非UTF-8编码. 它们是二进制的。 我想为每个字节显示一个字符。因此,任何进行UTF编码或解码的解决方案在我的上下文中都是行不通的。
  2. 代码是在一个高性能的循环中,因此我想避免分配内存。

谢谢你的帮助

rust lifetime
1个回答
1
投票

std::Formatter 已经实现了 Write所以你可以通过调用你的 render 方法中直接使用。

impl Display for DocObj {
    fn fmt (&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.render (f).or (Err (fmt::Err))
    }
}

1
投票

如果你想在一个结构中封装一个对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)
        }
    }
}

游戏场


0
投票

在外来类型上实现外来特征的一种方法是使用 新类型 模式。这个模式包括定义一个只包含一个元素的 "元组结构"(在本例中,是外来类型)。

它基本上与你试图实现的包装器相同(@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)
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.