Go言語100Tips ありがちなミスを把握し、実装を最適化する の No.45から47を読んだ。第6章 関数とメソッドの続き。
No.45はnilレシーバについて。Goでは、ポインタレシーバとしてnilが使えるが、一般的にはバグであることが多い。Goの場合、メソッドは第1引数をレシーバとする関数のシンタックスシュガーにすぎないのでnilレシーバはコンパイルエラーにはならない。これはちょっとややこしいので説明が難しい。ポインタのゼロ値はnilなので、それをreturnに指定するとnilレシーバが返ることになり、nil値を期待いていた場合、わかりにくいバグを生むことになる。ポインタを初期化した状態でreturnに指定しないようにしよう。
No.46はio.Readerの話。ファイルを読み込んで何かをする場合、引数にファイル名を指定すると、そのファイルへの操作しかできない。Goには io.Readerという素晴らしい入力を扱うインターフェースがある。これで入力を抽象化しておけば、ファイルではない入力(例えば、文字列やHTTPリクエストなど)の場合でもその関数は使える。そして、テストも書きやすい。io.Readerは素晴らしい。
No.47 は defer の引数評価について。deferはとても便利だが、その動きは理解していないとバグになりやすい。deferを使った時点での引数はすぐに評価されるため、実際に実行されるまでにその引数に指定した変数の値が変わったとしても、deferを使った時の値で実行される。deferは遅延実行なのだ。解決方法としては、引数にポインタを使う。そうすれば、ポインタは変わらずとも、ポインタの参照先は変わることができる。また、deferにクロージャを使うことも有効とのこと。クロージャの引数は同様にすぐに評価されるが、クロージャの中で外の変数を使う場合は、実行時の値を使ってくれる。レシーバも同様なので、値がreturnまでに変更される場合は、ポインタレシーバを使う。
この辺りも、よく間違いそうなところなので、忘れないようにしなければならない。