Golang で関数のデフォルト引数を指定する
概要
Ruby で関数のデフォルト引数を設定する場合は以下のように指定できます。
1 | def hoge(a, b = 2) |
Golang だと以下の様なデフォルト引数の定義ができません。
1 | func hoge(a, b = 2 int) string { |
こんな時にどうしようかと探っていると、 Functional Option Pattern に当たりました。
Functional Option Pattern
以下 2 記事が有名で必読です。
- Rob Pike: Self-referential functions and the design of options at 2014-01-24
- Dave Cheney: Functional options for friendly APIs | Dave Cheney at 2014-10-17
任意の値を定義する・しないで関数を分けると引数分、メソッドが増え、煩雑になります。
1 | const defaultB = 2 |
こういった煩雑さを解決する Golang の特性を活かした解決法が Functional Option Pattern です。
Functional Option Pattern で書き換え
1 | package main |
デフォルト引数の指定が効いているのがわかります。
Option
という引数に *configs
の設定を持つ関数を定義し、 その関数で各値を任意で設定することで、デフォルトの設定を上書いています。
こうすることで、 設定したい任意の値だけを設定し、その他はデフォルト値を参照する、ということができます。
よし完璧だ!と思ったら…テストでこける。。。
1 | fmt.Println(reflect.DeepEqual(WithB(12), WithB(12)) // false |
WithB
は関数型が返るので、 reflect.DeepEqual
では false になります。
reflect.DeepEqual
のコードを見てみると関数型 (reflect.Func
) で Nil でなければ、 false が返るようになってます。
コメントにある // Can't do better than this:
が推して知るべし。
https://github.com/golang/go/blob/master/src/reflect/deepequal.go#L126-L131
Functional Option Pattern にもう一手間加える
1 | const ( |
WithB
は int 型の値を返し、 reflect.DeepEqual
は true を返します。
Option を interface
で定義することで、どの様な型にでも呼び出せるのを利用しつつ、
Apply メソッドを定義して、 configs の上書きを図る、という算段です。
以下 googleapis/google-api-go-client で定義されている Functional Option Pattern が素敵だったので、こちらとても参考になります。
https://github.com/googleapis/google-api-go-client/blob/master/option/option.go
まとめ
軽い気持ちで関数のデフォルト引数の設定どうやるんだろうか?
と調べたら思わぬ展開になって、奥深さを感じました。
以上
ご参考になれば幸いです。
Golang で関数のデフォルト引数を指定する