Debug traitを実装するまでの道のり

# Debug traitを実装したかった
Rustの[The book](https://doc.rust-lang.org/book/)をとりあえず一周して、読みかけだった[プログラミングRust](https://www.oreilly.co.jp/books/9784873118550/)を見返しているところで、サンプルコードを自分なりにいじってみました。やりたかったことは単なる`Debug`トレイトの実装なのですが・・・。


## 動機

プログラミングRust, p.91-92にあったサンプルコードを見て、ふと関数`show`をDebugトレイト実装してやればいいんじゃないかと思ったのがキッカケです。

## サンプルコード
そのまま引用します。

```rust
use std::collections::HashMap;

type Table = HashMap<String, Vec<String>>;

fn show(table: Table) {
    for (artist, works) in table {
        println!("works by {}:", artist);
        for work in works {
            println!(" {}", work);
        }
    }
}

fn main() {
    let mut table = Table::new();
    table.insert(
        "Gesualdo".to_string(),
        vec![
            "many madrigals".to_string(),
            "Tenebrae Responsoria".to_string(),
        ],
    );
    table.insert(
        "Caravaggio".to_string(),
        vec![
            "The musicians".to_string(),
            "The Calling of St. Matthew".to_string(),
        ],
    );
    table.insert(
        "Cellini".to_string(),
        vec![
            "Perseus with the head of Madusa".to_string(),
            "a salt cellar".to_string(),
        ],
    );
    show(table);
}
```

`HashMap<String, Vec<String>>`に対して`Table`というアイリアスを設定していますね。これに対して`show`という`Table`の中身をコンソールに出力する関数を作っていますこの実行結果は次のようになります。


```sh
$ cargo run
...
works by Cellini:
 Perseus with the head of Madusa
 a salt cellar
works by Caravaggio:
 The musicians
 The Calling of St. Matthew
works by Gesualdo:
 many madrigals
 Tenebrae Responsoria
```

確かに`Table`の中身が表示されていますね。

## show関数いる・・・？

そう言えばRustには`Debug`トレイトがあるので、それを実装してやれば・・・という気の迷いを起こしたのがすべてのことの始まりです。まぁ、`Table`が`HashMap<String, Vec<String>>`へのエイリアスな時点で筋が悪そうなんですが、コンパイラのご指導のもと四苦八苦してみたのでよければお付き合いください。目標は次のコードで中身をコンソールに出力することです。

```diff
- show(table);
+ println!("{:?}", table);
```

## Debugトレイトを実装

それでは早速[Debug trait](https://doc.rust-lang.org/std/fmt/trait.Debug.html)トレイトを実装してみます。

```Rust
use std::fmt;

impl fmt::Debug for TableWrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (artist, works) in self {
            writeln!(f, "works by {}:", artist)?;
            for work in works {
                writeln!(f, " {}", work)?;
            }
        }
        Ok(())
    }
}
```

これをもとのコードに追加してコンパイルすると・・・。

```sh
$ cargo build
...
rror[E0119]: conflicting implementations of trait `std::fmt::Debug` for type `std::collections::HashMap<std::string::Str
ing, std::vec::Vec<std::string::String>>`:
 --> src/main.rs:7:1
  |
7 | impl fmt::Debug for Table {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: conflicting implementation in crate `std`:
          - impl<K, V, S> std::fmt::Debug for std::collections::HashMap<K, V, S>
            where K: std::cmp::Eq, K: std::hash::Hash, K: std::fmt::Debug, V: std::fmt::Debug, S: std::hash::BuildHasher;


error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
 --> src/main.rs:7:1
  |
7 | impl fmt::Debug for Table {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate
  |
  = note: the impl does not reference only types defined in this crate
  = note: define and implement a trait or new type instead
...
```

ふむ、`HashMap`がもとから持っている`Debug`トレイトの実装とぶつかってコンパイルが通りませんでした。そりゃそうか。

## そうだラッパーをかませよう

`Table`というのが構造体ではなく、標準ライブラリの`HashMap`へのエイリアスだったのが問題だったので、`Table`に対してラッパーを間に挟めばいいのでは・・・。

```Rust
struct TableWrapper {
    table: Table,
}
```
これに合わせてほかも少し変えておきます。

```diff
- impl fmt::Debug for Table {
+ impl fmt::Debug for TableWrapper {

-        for (artist, works) in self {
+        for (artist, works) in &self.table {
```

`Debug`トレイトでオブジェクトの所有権を奪うわけにはいかないので、しれっと借用に変更しています（`&self.talbe`の部分）。コンパイルはというと・・・。

```sh
$ cargo build
...
warning: struct is never constructed: `TableWrapper`
 --> src/main.rs:6:1
  |
6 | struct TableWrapper {
  | ^^^^^^^^^^^^^^^^^^^
  |
  = note: #[warn(dead_code)] on by default
```

ふむ、とりあえず通りましたね。

## 立ちはだかる壁

さて、それではもとの実装を置き換えて行きましょう。`Table::new()`を置き換えるために、`TableWrapper::new()`を実装しておきます。

```Rust
impl TableWrapper {
    fn new() -> TableWrapper {
        TableWrapper {
            table: Table::new(),
        }
    }
}
```
準備を済ませたぞ。オブジェクトを変更するために、

```diff
- let mut table = Table::new();
+ let mut table = TableWrapper::new();
```
と書き換えてからの、いざぅｔ。

```sh
$ cargo build
...
error[E0599]: no method named `insert` found for type `TableWrapper` in the current scope
  --> src/main.rs:41:11
   |
6  | struct TableWrapper {
   | ------------------- method `insert` not found for this
...
41 |     table.insert(
   |           ^^^^^^

...
```

ハイ、たくさんエラーが出ます。それもそのはず、ラッパーからだと`HashMap`の`insert`を直接呼び出せないですよね。

## Derefを実装してやれば・・・

The bookのどこかで見かけたのですが、Rustの演算子`.`は実はかなりいろいろなことをやってくれるらしいです。もし`TableWrapper`が`insert`を実装していなくても、`Deref`トレイトを実装していればそちらを探しに行ってくれるそうです。

### Derefで？

残念ながら小生はこのあたり、まだうまい説明が出来ないので興味がある方は[こちら](Implicit Deref Coercions with Functions and Methods)をご参照ください。

### Derefを実装

```Rust
use std::ops::Deref;
impl Deref for TableWrapper {
    type Target = Table;
    fn deref(&self) -> &Table {
        &self.table
    }
}
```

を実装します。もう`show`関数は使えないので、今回のゴールである形に書き換えておきます。

```diff
- show(table);
+ println!("{:?}", table);
```

コンパイルすると・・・。

```sh
cargo build
...
warning: variable does not need to be mutable
  --> src/main.rs:48:9
   |
48 |     let mut table = TableWrapper::new();
   |         ----^^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: #[warn(unused_mut)] on by default

error[E0596]: cannot borrow data in a `&` reference as mutable
  --> src/main.rs:49:5
   |
49 |     table.insert(
   |     ^^^^^ cannot borrow as mutable
...
```

ぐぬぬ。

### DerefMutを実装
だいぶ近いところまで来た気はするのですが、`Deref`はimmutableな参照を返すので、実装すべきは`DerefMut`トレイトのようでした。

```diff
- use std::ops::Deref;
+ use std::ops::{Deref, DerefMut};
```

```Rust
impl DerefMut for TableWrapper {
    fn deref_mut(&mut self) -> &mut Table {
        &mut self.table
    }
}
```

これでようやくビルドが通るようになりました。

## まとめ

`Debug`トレイトを実装してみたかっただけなのにだいぶ遠回りしてしまいました。ただ、コンパイラが優しいので、ひとつずつ手直ししていけばゴールにたどり着けるかもしれません。ただ、基本的にずっと怒られっぱなしなので、やっぱりRustってM気質な人向けの言語・・・（知らんけど。

## コードの全体図

最終的に次のようなコードになりました。

```Rust
use std::collections::HashMap;
use std::fmt;
use std::ops::{Deref, DerefMut};

type Table = HashMap<String, Vec<String>>;

struct TableWrapper {
    table: Table,
}

impl TableWrapper {
    fn new() -> TableWrapper {
        TableWrapper {
            table: Table::new(),
        }
    }
}

impl Deref for TableWrapper {
    type Target = Table;
    fn deref(&self) -> &Table {
        &self.table
    }
}

impl DerefMut for TableWrapper {
    fn deref_mut(&mut self) -> &mut Table {
        &mut self.table
    }
}

impl fmt::Debug for TableWrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (artist, works) in &self.table {
            writeln!(f, "works by {}:", artist)?;
            for work in works {
                writeln!(f, " {}", work)?;
            }
        }
        Ok(())
    }
}

fn main() {
    ...
    println!("{:?}", table);
}
```