Goのreflectパッケージはいつ使うのか
Goを書いていて、実際のユースケースに遭遇するまでメリットが実感できないものとして、reflectパッケージが挙げられると思います。
実際のところ、reflectパッケージで取れる情報についてはいろんな記事でまとまっているものの、じゃあそれを使って何ができるんだ、実益があるのかという点を体感する機会は人によりけりです。
よくある説明と違和感
「メタプロ的なことができるぜ」「構造体の情報を取得してあれこれできるぜ」という説明を受けることがありました。
ただ、僕的にはいまいちイメージが掴めずにいました。おまけにパフォーマンスに負の影響があるから基本的には使わないものとして認識している方が多いと思います。
lestrratさんの少し前の資料とかを見て、そんな感じのイメージでいました。
Goを学習した時に、goroutineとかは利用場面に比較的に早くから遭遇できる一方、reflectパッケージはいつになったら用途が発生するのやら、という。
そんなこんなで学習し始めの頃迷っていたりしたのだが、今振り返って見るとユースケースは多く、同じようなモヤモヤを抱いている方がいれば、多少の参考になるのでは、と思いここにまとめます。
reflectパッケージでできること
そもそもreflectパッケージでできることはなんなのでしたっけ。公式ドキュメントを見ると次のように書いてあります。
Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. The typical use is to take a value with static type interface{} and extract its dynamic type information by calling TypeOf, which returns a Type.
つまるところ任意の型情報を用いてオブジェクトに変更を加えることができる、と。これだけではまだ具体的な利用ケースが思い浮かびません。
ので、自分が今まで遭遇したreflectを使ったライブラリや、自分で利用したときの事例を大別して、次の順に見ていきます。
- 付加的な情報を取得したい場合
- 構造体の中身を取得したい場合
- 実装状態を取得したい場合
です。
付加的な情報を取得したい場合
これはencoding系の標準パッケージを読めばよく遭遇するケースだと思います。例えば、json, yamlなどですね。
標準パッケージでなくても、tomlなどのパースをする場合はほぼ必ず遭遇すると思います。あとはGAE/Goでよく使うDatastoreクライアントのgoonでも、下記のようにタグ情報を取得します。
フィールド本体ではなく、あくまでタグとして付加的な情報を記載する場合は、これが有効になると思います。また、標準のTypeでは表現に乏しくても、タグ情報ならできる表現の幅があるかと思います。
構造体の中身を取得したい場合
これが一番ユースケースとしては多いと思いますが、値のフィールド情報や関数の情報を取得する場合があります。標準パッケージだと、templateなどで、値を受け取ってそれを個別に表示して行くところの処理とかで使われます。
標準パッケージ以外で見てみると、主に静的解析や値をダンプしたい時などに見ることがあります。有名どころだとtenntennさんの静的解析の資料に載っているパッケージたちや、k0kubun/ppなどでしょうか。
実装状態を取得したい場合
aws-lambda-go - Libraries, samples and tools to help Go developers develop AWS Lambda functions.github.com
あくまで個人的な意見ですが、こういった「interface{}がどういう実装を持っているか」というのは、プロジェクト間の差異を吸収する時に使えると思います。
特定の実装を持っていれば、バリデーションが通ったあとに実行するという手順を踏むことで、APIが適切に実装しているかを、そのAPIに幅を持たせながらチェックすることができます。
まとめ
個人的に観測した範囲でのreflectパッケージ使い道ですが、あくまで私のなかでの大別であって、もっと用途に幅はあると思います。
ただ、Goを書いてて自作ツールとかでたまに出会うことがあっても、普通にサーバーサイドの実装をしているだけだと用途が見出せないもののうちの一つだと思いますので、みなさんがみなさんなりに、reflectの出現場面を考えてみる機会の一助となればと思います。