RustでDisplayトレイトを実装する際に与えられたフォーマット指定を使う方法

## 概要
構造体などのフィールドをフォーマットするときに、`println!`マクロなどで与えられたフォーマット指定をそのまま使ってフォーマットしたいという状況にあるとします。このとき、フィールドの[`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)トレイトの`fmt`メソッドに`Formatter`を渡して呼び出すことで、与えられたフォーマット指定を使って値をフォーマットすることができます。つまり、次のような形で実装します。

```rust
impl fmt::Display for MyData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.field.fmt(f)
    }
}
```

## はじめに
数値型をフィールドに持つ構造体に[`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)トレイトを実装するときに、パディングや精度などのフォーマット指定を受け付けられるようにしたいと思います。これを実現するためには、フィールドの`fmt`メソッドを呼び出せば良いということを、具体例を使って紹介します。

`Display`トレイトの実装例は[Rust By Example 日本語版](https://doc.rust-jp.rs/rust-by-example-ja/index.html)でも紹介されていますが[^1]、これらの例は`write!`などのマクロを呼び出す際に、`Display`トレイトの実装の中でフォーマット文字列を新たに指定しているため、自ら実装した`fmt`メソッドに与えられたフォーマット指定は無視されてしまいます。

表題の目的を実現する方法について書いている記事は、少なくとも日本語では見当たらなかったのでこの記事を書くことにしました。なお、この記事のソースコードはRust 1.48.0で動作を確認しました。

## 例

平面上の点を表現する構造体`Point`を定義したとしましょう。

```rust
struct Point {
    x: f64,
    y: f64,
}
```

これに`Display`トレイトを実装して、次のコードが通るようにしたいのです。

```rust
    let pt = Point { x: 0.5, y: (3.0_f64).sqrt()/2.0 };

    assert_eq!(format!("{}", pt),                "(0.5, 0.8660254037844386)".to_owned());
    assert_eq!(format!("{:.6}", pt),             "(0.500000, 0.866025)".to_owned());
    assert_eq!(format!("{:.prec$}", pt, prec=3), "(0.500, 0.866)".to_owned());
    assert_eq!(format!("{:>8.4}", pt),           "(  0.5000,   0.8660)".to_owned());
```

まずは素朴に`Display`トレイトを実装してみましょう。

```rust
impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}
```

[このソースコード](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f3e78a3f6af8b97d06de70c684fa1ff2)を実行してみると以下の結果が得られます。

```
   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.23s
     Running `target/debug/playground`
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"(0.5, 0.8660254037844386)"`,
 right: `"(0.500000, 0.866025)"`', src/main.rs:16:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

どうやら2番目の`assert_eq!`でのフォーマット指定が無視されているようです。どうしてこうなったのでしょう？それは、今実装した`Display`トレイトでは`write!`マクロでのフォーマット指定を`{}`としたので、デフォルトのフォーマット方法で出力されたからです。

`write!`マクロは[`Write`](https://doc.rust-lang.org/std/fmt/trait.Write.html)トレイトの`write_fmt`メソッドを呼び出します。`write_fmt`メソッドは[`std::fmt::write`](https://doc.rust-lang.org/core/fmt/fn.write.html)関数を呼び出します。そして、`write`関数は[新しく`Formatter`を作り、それを使って値をフォーマットします](https://doc.rust-lang.org/src/core/fmt/mod.rs.html#1063-1101)。

今、我々に与えられた`Formatter`は、`Point`構造体の`fmt`メソッドの引数である`f`です。これを使うように`Display`トレイトの実装を修正しましょう。各フィールドの`fmt`メソッドに`f`を渡して呼べばOKです。少し冗長ですが、修正した実装は以下のようになります。

```rust
impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "(")?;
        self.x.fmt(f)?;
        write!(f, ", ")?;
        self.y.fmt(f)?;
        write!(f, ")")
    }
}
```

[このソースコード](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4616a18b811cb61b88ee8c6eb75afae8)を実行してみましょう。パニックせずに終了します。これで、自分で実装した`Display`トレイトで与えられたフォーマット指定を使うことができました。

[^1]: https://doc.rust-jp.rs/rust-by-example-ja/hello/print/print_display.html
    https://doc.rust-jp.rs/rust-by-example-ja/hello/print/fmt.html