【Go】型が特定のinterfaceを満たしているかをコンパイル時に確認させる方法

# はじめに
こんにちは、kenです。お仕事ではGoを使っています。
先日、[Effective Go](https://go.dev/doc/effective_go)（Goの公式が出している慣用的なコーディングスタイルと実践を記したドキュメント）を読んでいたら[興味深いこと](https://go.dev/doc/effective_go#blank_implements)が書かれていたので今日はそれについて書こうと思います。

内容は「**型が特定のinterfaceを満たしているかをコンパイル時に確認させる方法について**」です。
# Goのinterfaceについておさらい
本題に入る前にGoのinterfaceについてざっくり説明しておきます。interfaceについてもう知っているよという方はスキップしてください。

Go言語ではinterfaceをメソッドシグネチャの集合として定義します。
```go
type hogeIF interface {
	Method1()
	Method2()
	... 
	MethodN()
}
```
そして型がinterfaceを実装する際には、interfaceの定義に使ったメソッドを型に実装するだけでよく、「**その型がどのinterfaceを実装しているか**」については明示的に宣言をする必要がありません。つまりGoのinterfaceの実装は暗黙的なのです。

ただ暗黙的であるがゆえに、ある型が自分の望んだinterfaceを実装しているかはややわかりにくいところがあります。
たとえば型`foo`が`hogeIF`インターフェースを満たしているのか？を確認するためには、型`foo`に実装しているメソッドに`method1`,`method2`, ... , `methodN`が存在するかをチェックする必要があります。

# Goのinterfaceの実装は暗黙的...。だけどそれで困ることは少ない
しかし、このデメリットは多くの場合問題にはなりません。
というのも、ある型が自分の望んだinterfaceを実装しているかは静的解析時・もしくはコンパイル時に検出できるからです。

例えば、`io.Reader`インターフェースを引数に期待する関数へある構造体Aを渡す場合、構造体Aが`io.Reader`インターフェースを実装していなければコンパイルは通りませんし、静的解析時にもエラーが出るでしょう。つまり型が特定のinterfaceを満たしているかどうかは使用時に気づくことができるので、普段この仕様に不便さを感じることはまずありません。

# でもごくたまに困るときもある
しかし、以下のような特殊なケースでは型が特定のインターフェースを実装しているかどうかを確認したくなるときもあります。
## 【Case1】 とある構造体に`json.Marshaler`インターフェースを実装しておいてほしい場合

`json.Marshall`関数は構造体をJSON形式に変換する処理を行います。

このとき、変換対象の構造体に`json.Marshaler`インターフェースが満たされている場合（つまり`MarshalJSON`メソッドが実装されている場合）は、**実装されている**`MarshalJSON`を用いてJSONへの変換を行い、そうでない場合は**デフォルト**の変換形式を用いて変換を行います。[^1]

[^1]:本当はここの処理はもう少し複雑で、`json.Marshaler`インターフェースが満たされていない場合は`encoding.TextMarshaler`インターフェースを実装しているかを調べて...といったようなことを行うのですが、ここでは簡単のために省略します。より詳しいことを知りたい方は[こちら](https://pkg.go.dev/encoding/json#Marshal)から

もしデフォルトの変換形式を使いたくない事情があり、独自の形式でJSONへの変換を行いたい場合は`json.Marshaler`インターフェースが満たされているか、つまり独自に定義した`MarshalJSON`が存在するかを確認する必要があります。

以下のコードでは、Person構造体が`json.Marshaler`インターフェースを満たしている場合と満たしていない場合とで、JSONへの変換形式が変わってきます。


```go
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string
	Age  int
}

/*
	func (p Person) MarshalJSON() ([]byte, error) {
		return json.Marshal(&struct {
			Name     string `json:"name"`
			Age      int    `json:"age"`
			Greeting string `json:"greeting"`
		}{
			Name:     p.Name,
			Age:      p.Age,
			Greeting: fmt.Sprintf("Hello My name is %s", p.Name),
		})
	}
*/



func main() {
	p := Person{
		Name: "ken",
		Age:  23,
	}

	jsonData, err := json.Marshal(p)
	if err != nil {
		fmt.Println("JSON Marshaling failed:", err)
		return
	}

	fmt.Println(string(jsonData))
	// json.Marshalerインターフェースを満たしている(=MarshalJSONメソッドを実装している)場合
	// {"name":"ken","age":23,"greeting":"Hello My name is ken"}

	// json.Marshalerインターフェースを満たしていない場合
	// {"name":"ken","age":23}
}
```

`json.Marshaler`インターフェースを満たしていても満たしていなくてもコンパイルはできてしまうので、独自の形式でJSONへの変換を行いたい場合は型が`json.Marshaler`インターフェースを実装しているかわざわざ確認しにいく必要があります。

## 【Case2】 とある構造体に`fmt.Stringer`インターフェースを実装しておいてほしい場合

構造体をログなどにカスタマイズした形で出力したい場合、`%s`の書式指定子を使って構造体の情報を表すことが稀にあると思います。

その際、構造体に`String`メソッドを実装していない場合（つまり`fmt.Stringer`インターフェースを構造体が満たしていない場合）は読みにくい形でログ出力される恐れがあります。

また、もともとログ出力のために`String`メソッドを実装していても、その事情を知らない人がリファクタリングの途中で使っていないメソッドだと思って消してしまうこともあるでしょう。それを避けるためには、やはりコンパイル時に構造体が`fmt.Stringer`インターフェースを満たしているかを確認する必要が出てきます。

以下のコードでは、Person構造体が`fmt.Stringer`インターフェースを満たしていない場合に読みにくい文字列が出力されてしまっています。
```go
package main

import "fmt"

type Person struct {
	Name string
	Age  int
}

/*
	func (p Person) String() string {
		return fmt.Sprintf("Name: %s, Age: %d", p.Name, p.Age)
	}
*/

func main() {
	p := Person{
		Name: "ken",
		Age:  23,
	}
	fmt.Printf("%s\n", p)
	// Personがfmt.Stringerインターフェースを満たしていない(=Stringメソッドを実装していない)場合： {ken %!s(int=23)}
	// Personがfmt.Stringerインターフェースを満たしている(=Stringメソッドを実装していない)場合：   Name: ken, Age: 23
}

```

こちらも`Person`型が`fmt.Stringer`インターフェースを満たしているかどうかにかかわらずコンパイルはできてしまうので、独自の形式でログ出力したい場合は注意する必要があります。

# どのようにして解決するか
ではどのようにして型が特定のInterfaceを満たしているかをコンパイル時に確認するかというと、次の一行をどこかに挟むだけです。
```go
var _ <interface> = (*<Type>)(nil)
```

先ほどのCase1,2の場合で書くと、それぞれ次の一行を挟むだけです。
```go: ケース1
var _ fmt.Stringer = (*Person)(nil) // Personがfmt.Stringerインターフェースを満たしていることを保証する
```

```go: ケース2
var _ json.Marshaler = (*Person)(nil) // Personがjson.Marshalerインターフェースを満たしていることを保証する
```

なぜこの一行を挟むだけで良いのかを、ケース2の場合を用いて説明します。

この宣言は、`json.Marshaler`インターフェース型のアンダースコア変数に`Person`のポインタ型の`nil`値を代入しようとしています。
この宣言のコンパイルが成功する場合、`Person`型が`json.Marshaler`インターフェースを実装しているとコンパイル時に検証されたことを意味します。ここでのポイントは、`Person`型（正確にはそのポインタ型）が`json.Marshaler`インターフェースの要求を満たすメソッドを実装しているかどうかを確認することであり、実際に実行時に何かが代入されるわけではなく、型の互換性を確認するためだけの構文ということです。

これにより、開発者は`Person`が意図した通りに`json.Marshaler`インターフェースを実装していることを確実にすることができます。また、将来`json.Marshaler`インターフェースが変更された場合にも、この宣言によって影響を受ける箇所が明確になるため、迅速な修正が可能になるというメリットもあります。

これで型が特定のinterfaceを満たしているかを実行時ではなく、コンパイル時に確認できるようになりました！
めでたし、めでたし〜



# あれ、どうしてPersonのポインタ型を使ってチェックしているの？？
今回はこれで終わりではありません。
Effective Goではこれで終わりなんですが、私にはどうしても気になる点がありました。それは**わざわざPersonのポインタ型を使っている点**です。

```go
var _ json.Marshaler = (*Person)(nil)
```
先ほど出てきた↑のコードですが、どうしてPersonをわざわざポインタ型にして検証しているのだろう...と思いませんでしたか。(私は思いました)

`Person`型が`json.Marshaler`インターフェースを満たしているのかを確認するだけなら↓のコードでも良いはずです。
```go
var _ json.Marshaler = Person{}
```

しかしこの２つのコードには明確な違いがあり、Effective Goに記載はないですがうまく使い分ける必要があると私は思っています。今からその違いと使い分けが必要になる理由について順に説明していきます。

（ここから先は完全に私個人の考察と見解です、正確性には気をつけて書いていますが間違っている場合はご指摘ください）

突然ですが、Case2で`Person`の値レシーバに対して定義していた`MarshalJSON`メソッドを、**ポインタレシーバ**に対して定義するとどうなるでしょうか。
```go
package main

import (
	"encoding/json"
	"fmt"
)

type Person struct {
	Name string
	Age  int
}

// ポインタレシーバに変更
func (p *Person) MarshalJSON() ([]byte, error) {
	return json.Marshal(&struct {
		Name     string `json:"name"`
		Age      int    `json:"age"`
		Greeting string `json:"greeting"`
	}{
		Name:     p.Name,
		Age:      p.Age,
		Greeting: fmt.Sprintf("Hello My name is %s", p.Name),
	})
}

func main() {
	p := Person{
		Name: "ken",
		Age:  23,
	}

	jsonData, err := json.Marshal(p)
	if err != nil {
		fmt.Println("JSON Marshaling failed:", err)
		return
	}

	fmt.Println(string(jsonData))
}
```
これを実行すると
```
{"Name":"ken","Age":23}
```
が出力されます。

独自で定義した`MarshalJSON`が使われていませんね。ただこれは`json.Marshall`の引数に渡したものがPersonの値型でありポインタ型ではないので当たり前といったら当たり前です。`Person`の**ポインタ型**には`MarshalJSON`メソッドが実装されているものの、`Person`の**値型**には`MarshalJSON`メソッドは実装されていないので`json.Marshaler`インターフェースは満たされていません。よってデフォルトの変換形式が使われます。
なので独自のJSON形式を使いたい場合は`json.Marshal`にわたす引数をポインタ型にして`json.Marshal(&p)`とする必要があります。

さて、ではこのコードの中に
```go
var _ json.Marshaler = (*Person)(nil)
```
を挟むとどうなるでしょうか。これまでの説明だと、このコードのコンパイルが通るのは「Person構造体が`json.Marshaler`インターフェースを実装している場合」に限るということでした。そして今、Personの値型は`json.Marshaler`インターフェースを満たしていないためコンパイルは通らないはずです。

しかし↑の一行を挟んでも**コンパイルは通ってしまいます。**




<br>
<br>

*あれ？さっき`Person`型が`json.Marshaler`インターフェースを満たしているか確認するためには*
```go
var _ json.Marshaler = (*Person)(nil)
```
*を挟めば良いと書かれてあったけど、メソッドのレシーバがポインタ型になっているときは`Person`型が`json.Marshaler`インターフェースを満たしていない場合もあるってこと？*

<br>
<br>

はい。そのとおりです...。。

厳密に書くと`var _ json.Marshaler = (*Person)(nil)`のコンパイルが通るのは「`Person`のポインタ型が`json.Marshaler`インターフェースを満たしている場合」であって、「`Person`の値型が`json.Marshaler`インターフェースを満たしている場合」ではありません。

つまり整理すると「`Person`型から`json.Marshaler`インターフェースに定義されているメソッドを（**ポインタ型への変換も駆使して**）呼び出せる」場合にコンパイルが通るということです。

このような「ポインタ型が満たすinterfaceを値型は満たさない」という事象が起こるのはGo言語の仕様が関係しています。
Go言語ではメソッドが値レシーバとして定義されている場合、そのメソッドは値型とポインタ型の両方のメソッドセットに所属します。一方でメソッドがポインタレシーバとして定義されている場合、そのメソッドはポインタ型のメソッドセットにのみ所属します。つまりポインタ型が持つメソッドセットのほうが集合として大きいのです。[^2]
（このことはGo wikiの[Method Sets](https://go.dev/wiki/MethodSets)に書かれています。）

[^2]:メソッドセット（interfaceを満たしているかを判定するもの）の説明としてはこうなるのですが、実際には値型からもポインタレシーバのメソッドを呼び出せる場合があります。（暗黙的変換）
たとえば、ポインタレシーバのメソッドである`(p *Person) methodA()`は、`Person`構造体の値型変数`p`からは`p.methodA()`のように呼び出しが可能です。このとき`p.methodA()`は自動的に`(&p).methodA()`のように解釈されています。

![ポインタ型と値型のメソッドセット.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649218/ec9acfac-b2c6-8154-f173-8bbc9c78bf4f.jpeg)

---
今回の記事のタイトルは、「型が特定のinterfaceを満たしているかをコンパイル時に確認する方法」であり、Effective Goでの章名もこのようなものでした。しかし上で見たように、この「型」という表現の中に「値型」と「ポインタ型」が混在する点には注意が必要です。必ずしも「値型」単体がinterfaceを満たしているとは限りません。
繰り返しになってしまいますが、`var _ json.Marshaler = (*Person)(nil)`のコンパイルが通るのはポインタ型のメソッドセット（値レシーバのメソッドとポインタレシーバのメソッドを合わしたもの）がinterfaceの定義を満たしている場合です。

ポインタ型は値レシーバのメソッドセットとポインタレシーバのメソッドセットを合わせ持つ一方で、値型は値レシーバのメソッドセットしか持たないという仕様は、一見するとややこしく不親切に感じられます。しかし値型もデリファレンスを行うことでポインタレシーバのメソッドを呼び出せるため、困る瞬間があるかというとそんなにないと思われます。また注記2に書いた暗黙的変換もあるためコードを書いてる際に疑問に思うタイミングも少ないことでしょう。

ただしCase1のときのように、型が特定のinterfaceを満たしているかが厳密に判定される際にはこの仕様は少々厄介です。
実は`json.Marshal`に渡された引数が`json.Marshaler`インターフェースを満たしているかは`_, ok := val.(json.Marshaler);`のような型アサーションを使って判定されます。すなわち、引数の型のメソッドセットに`MarshalJSON() ([]byte, error)`のシグネチャのメソッドが含まれている必要があります。
よってただ「（必要に応じたデリファレンスを行うことで）`MarshalJSON()`を呼び出せる」というだけでは不十分なのです。デリファレンスをせずに直接`MarshalJSON()`を呼び出せることが必要です。ややこしいですが`MarshalJSON()`が呼び出せるかと、`MarshalJSON()`がメソッドセットに含まれているかは別物であることに注意しましょう。


なので私個人的には、値型をレシーバとしてメソッドを基本的に書いていく場合は
```go
var _ json.Marshaler = Person{}
```
のように書くのもアリだと思います。なぜならこのように書くとポインタレシーバで記述されたメソッドがインターフェースの構成要素としてカウントされないため、値レシーバのメソッドだけで`json.Marshaler`インターフェースが実装されているかがわかるためです。ポインタ型にデリファレンスする必要があるか気にしなくて良いため、思わぬ事故を防ぐ効果が見込めます。

# 終わりに
使用する機会は少なそうですが、引き出しとしてもっておきたい知識だなと思いました。
最後は少しくどくなってしまいましたが、自分のギモンを掘り下げていくことでドキュメントを読むだけでは気付けなかったようなことまで知ることが出来て面白かったです。

最後まで読んでいただきありがとうございました。間違いなどあればコメントにてご指摘ください。