Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to write a function that can output to either std::io::Write or std::fmt::Formatter?

Tags:

rust

I want to write a function that produces some text, like so:

fn produce_stuff(/* ??? */) -> Result<()> {
    write!(...);
    write!(...);
    write!(...);
    ...
}

I want to be able to use this function in two contexts:

  1. Use it to output to an IO writer—something like produce_text(io.stdout()).
  2. Use it as a helper function when implementing Display — something like this:
struct Foo { ... }
impl Display for Foo {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        ...
        produce_text(f);
        ...
    }
}

It seems like each of these use cases would require a different signature for produce_text. For #1, the function would need to take something that implements std::io::Write, and for #2 it would need to take a std::fmt::Formatter.

I suppose I could write it as a macro instead of as a function (similar to how write! is a macro that works both in both contexts), but it somehow feels wrong to use a macro for this.

like image 844
Will Avatar asked Jul 31 '19 01:07

Will


1 Answers

You just have to implement Display.

When your Foo implements Display, you can use it with any implementation of Write, including io::Stdout:

use std::{
    io::{self, Write},
    fmt,
};

struct Foo {
    field: u16,
}
impl fmt::Display for Foo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Foo({})", self.field)
    }
}

fn main() {
    write!(io::stdout(), "{}", Foo{field: 3}).unwrap();
}

You don't have to write your own macro; just use write!. This should not feel wrong, it's the normal way.

There's no runtime cost related to the "{}" argument: it's parsed at compile time (in a compiler built-in), which makes the write!(w, "{}", displayable) call efficient.

playground

like image 165
Denys Séguret Avatar answered Jan 02 '23 13:01

Denys Séguret