Go のインターフェースと構造体とその他諸々
Go の諸々の基礎をおさらいした。
インターフェースと構造体
今 Go で OGP 画像を生成するツールを作っているのだが、参考になるものをググっていたら似たようなことをしている人を見つけた。
https://github.com/Iwark/text2img
改行まではサポートしてないように見えるが、画像に文字を入れるところは完全に同じようなコードになりそう。
ということでこのコードを読んでいたのだが、メインとなるパッケージでインターフェース Drawer と構造体 drawer を定義していた。
- Go におけるインターフェースってどうやって実装するんだっけ?
- そしてインターフェースを実装しているのがたった一つの構造体だけだとしてもインターフェースを定義するメリットはあるのか?
このあたりがよくわからなかったので調べた。
ググると “Accept interfaces, Return structs” というセンテンスがすごくよく出てくるのだが、それがどこに書いてあるかがわからない。が、そのセンテンスの説明として引用されているコードレビューコメントに関する記載は発見できた。
https://github.com/golang/go/wiki/CodeReviewComments#interfaces
そして上記の解釈としてわかりやすいと思った記事は下記。
GoのInterfaceの作法 “Accept Interfaces, Return structs”
Go においてはインターフェースの実装として implements みたいなキーワードは無く、インターフェースに定義されている関数を全部実装していればインターフェースを実装したとみなされる。
そしてインターフェースに実装されていない関数を実装していても問題ない。
そのため上記の記事のように、インターフェース実装側であるパッケージではいろんな関数を定義しているが、それを利用する側でインターフェースを定義することで不要な関数を見えなくすることができる。
このような使い方ならばインターフェースを実装しているのがたった一つの構造体だけだとしてもインターフェースを定義する意味はありそう。
ただ最初にあげた text2img というパッケージのような事例だと、インターフェースと構造体を同一パッケージに定義している上に、構造体が持ってる関数は全部インターフェースに定義してあるっぽいので、あまり意味はなさそう。
ということで私の自作ツールでは特にインターフェースを定義する意味がないのでインターフェースは定義しないことにした。
ポインタ
前述の コードレビューコメントに関する記載 で
The implementing package should return concrete (usually pointer or struct) types
とあるのだが、そういえばポインタとかいうのも正直雰囲気で書いているので改めて調べた。
A Tour of Go より
変数 T のポインタは、 *T 型で、ゼロ値は nil です。
var p *int
& オペレータは、そのオペランド( operand )へのポインタを引き出します。
i := 42 p = &i
- オペレータは、ポインタの指す先の変数を示します。
fmt.Println(*p) // ポインタpを通してiから値を読みだす *p = 21 // ポインタpを通してiへ値を代入する
で、このポインタをよく使うのは関数のレシーバなのだが、値渡しとの違いは
- 関数内で値を変更できるかどうか
とのこと。
また構造体のような大きなものをレシーバにする場合、メモリの節約的な観点でポインタ渡ししたほうがいいらしい。
おわり