Implementing a two line Display

Hey folks,

This is driving me absolutely nuts. This of course doesn’t compile:

impl Display for TestResult {
    fn fmt(&self, f: &mut Formatter<'\_>) -> std::fmt::Result {
        writeln!(f, "{}", self.main_line())

        if let Some(detail_line) = self.detail_line() {
            writeln!(f, "{}", detail_line)
        }
    }
}

How would I do this properly?

Thanks!

what's the error messsage? how is TestResult and main_line(), detail_line() defined? please provide more context.

I'm not sure I understand what your exact problem is, but I guess you want to do something like this?

struct TestResult {
    main_line: String,
    detail_line: Option<String>,
}

impl std::fmt::Display for TestResult {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "{}", self.main_line)?;

        if let Some(detail_line) = &self.detail_line {
            writeln!(f, "{}", detail_line)?;
        }

        Ok(())
    }
}

Playground.

Edit: forgot to add the ?s to the writeln! invocations to early return in case an error happens during a write.

2 Likes

Perfect, thank you!

Another possibility is something like this:

use std::fmt::{Display, Formatter};

fn main() {
    struct TestResult;
    impl Display for TestResult {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            let mut result = format!("{}", "hello");

            let detail_line = Some(" world");
            if let Some(detail_line) = detail_line {
                result.push_str(detail_line)
            }
            writeln!(f, "{}", result)
        }
    }
}

PS: Oh, I misunderstood the intention.

This looks like a syntax error? Should be <'_>

Oops. Was wondering why the syntax highlighting was off.

I just wanted to write two lines, this is the sketch so far:

impl Display for TestResult {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let summary = self.outcome.summary().to_string();
        let elapsed = format!("{}ms", self.elapsed.as_millis());
        let description = &self.description;

        writeln!(f, "{summary:7} {elapsed:6} {description}")?;
        writeln!(f, "  Something went wrong")?;
        Ok(())
    }
}

I just couldn't figure out what I was supposed to be returning from fmt(), not realising that I just need the Ok(()). Adding the ?s also avoids the compiler warnings.

It just seemed that every example on the world-wide-web had exactly one call to write!() or writeln()

I see. just a reminder, if you have trouble figure out what to return, the first thing to check is the type definition.

for example, if you lookup std::fmt::Result, it is an alias for Result<(), std::fmt::Error>

well, I guess most sample code assumes you already are familiar with error handling in rust, particularly the Result type.

again, a good place to look is how the trait is implemented in the standard library. for example, scrollong through the "Implementors" section of std::fmt::Display, you can find complex types such as PanicInfo or Backtrace, then you can simply click the "source" link, which will navigate to the source code of the impl block here:

1 Like

Well, it’s not just about warnings, you absolutely want and need to propagate any errors rather than ignoring them.

1 Like

Perhaps. But say we take the example here:

impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}, {})", self.0, self.1)
    }
}

And rewrite it like so:

impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Use `self.number` to refer to each positional data point.
        write!(f, "({}", self.0)?;
        write!(f, ", {})", self.1)?;
        Ok(())
    }
}

The error handling is equivalent, no?

In any case, would we expect an error to ever occur here?

1 Like

Yes an error could occur because this is the error of the formatter. One could use your debug impl to write into a fixed size buffer. A write!() could now result in an error if the buffer is full

Yes, but I meant that if you do something like

fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    // Use `self.number` to refer to each positional data point.
    _ = write!(f, "({}", self.0);
    _ = write!(f, ", {})", self.1);
    Ok(())
}

to silence the warnings, you're ignoring errors and lying to the caller by returning Ok.

When doing formatted output to an arbitrary io::Write, yes.

What sort of error would you expect in comparison to a println!() call?

The device being full (ErrorKind::StorageFull), for the easiest example.

1 Like

Thanks for that, just checking out the docs now.