Implement Display Trait with anyhow error

Is there a way to implement the display trait with an anyhow error directly?
Right now, I have this working version:

impl Keybindings {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<()> {
        let mut tw = TabWriter::new(vec![]);
        for (key, operations) in self.0.iter().sorted() {
            writeln!(&mut tw, "{}:\t{}", key, operations)?;
        }
        tw.flush()?;

        let written = String::from_utf8(tw.into_inner()?)?;
        f.write_str(&written)?;

        Ok(())
    }
}

impl fmt::Display for Keybindings {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.fmt(f).map_err(|_| fmt::Error)
    }
}

I know you will say that Display should never really fail, and I would agree. But in this case, I am using the tabwriter crate, which allows me to create elastic tabstops, and the functions I need to call there return errors, which I would prefer to propagate than to unwrap.
Any ideas?

No.

This type does not support transmission of an error other than that an error occurred. Any extra information must be arranged to be transmitted through some other means.

What would you recommend to do in my case then? I think the code right now is a little ugly with the indirection, and am open to suggestions on how to improve it.
I guess I could do the replacing of the tab character elsewhere, and then don't need the anyhow error anymore, but if there's any other suggestions you have I'm open to hearing them.

Based on the snippet, you could have a method on Keybindings that returns a Result<String, YourError> corresponding to returning the written value. (Or for more forward compatibility, some newtype around String in a private field / impl Display. Or if it makes sense, some other type that knows it can't produce any non-writing-related errors.)

Consumers would then have the choice of checking for richer errors or not.

You should take after tabwriter and make a function that writes to an implementer of io::Write. Display doesn't provide much for things that can't be printed inline anyway.

Also, you don't need to add &mut when passing something to writeln!, since the macro adds that already.

3 Likes

Do you have an example for the function signature of your proposed method?

fn write<W>(writer: W, what: MyType) -> std::io::Result<()>
where
    W: std::io::Write
3 Likes

Yeah the above, maybe with self instead of what

fn write<W>(&self, writer: W) -> std::io::Result<()>
where
    W: std::io::Write
2 Likes

Is this what you mean:

impl Keybindings {
    /// Write formatted version (insert elastic tabstops) to a buffer.
    fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
        let mut tw = TabWriter::new(writer);
        for (key, operations) in self.0.iter().sorted() {
            writeln!(tw, "{}:\t{}", key, operations)?;
        }
        tw.flush()?;
        Ok(())
    }

    fn fmt(&self) -> Result<String> {
        let mut buffer = vec![];
        self.write(&mut buffer)?;
        let written = String::from_utf8(buffer)?;
        Ok(written)
    }
}

impl fmt::Display for Keybindings {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let formatted = self.fmt().map_err(|_| fmt::Error)?;
        f.write_str(&formatted)
    }
}

There's no way to get rid of the intermediate fmt(&self)?

I was thinking of dropping Display entirely. Besides rewriting tabwriter to implement fmt::Write, there's not much you can do besides have the intermediate function.

Also don't take &mut W, just take W. Any W that implements Write will also have &mut W implement Write.

2 Likes

I can't really just drop the entirely, I still need to print this struct to stdout somewhere (i.e. need the display trait). Are you suggesting I should use another way of doing that, for instance saving the string representation of the Keybindings only once when I create it at the beginning, so I can do the error handling there?

You don't need Display for printing to stdout, since stdout is Write.

Actually sorry for the inaccuracy of my last message, I'm actually not printing to stdout but saving the string version of the keybindings as state in my app, so that it can be printed in a TUI help menu in a TUI I am working on. So I need ToString, which Display comes with.

String implements Display just fine, so it's not the printing that's the issue, it's generating the string. In which case, your fmt method will do that perfectly well. No need to implement Display for Keybindings. You can't use ToString since it can't return an error, and you wouldn't want to anyway since it would create two strings every time.