GolangでNamed Result Parametersを使っていつもより簡潔にエラー処理を書く
GolangでNamed Result Parametersを使った関数を書いているときに、ふと気づいたことがあったのでその話。
Named Result Parametersとは
関数の戻り値に名前をつけることができるGolangの文法で、日本語では「名前付き結果パラメータ」と呼ばれます(まんまやんけ)
戻り値につけた名前は、そのまま関数内で宣言なしで普通の変数と同様に使うことができます。
func Sum(args ...int) (sum int) {
for _, n := range args {
sum += n // 宣言なしでsumが使える!
}
return sum
}
そして、Named Result Parametersを使うとreturnの後ろは何も書かなくても勝手にその変数を戻り値にして返してくます。
func Sum(args ...int) (sum int) {
for _, n := range args {
sum += n
}
return // ここになにも書かなくても勝手にsumが返る!
}
このreturnの後ろを書く必要がない特徴を使うとエラー処理が簡潔に書けます。
エラー処理を簡潔に書く
普通はこんな感じでいつものイディオムで書いているところを、
func hoge() error {
err := // なにかの処理
if err != nil {
return err
}
return nil
}
関数の最後で行うエラー処理に限って、
func hoge() (err error) {
err = // なにかの処理
return
}
if err != nil{}を省略することができます。
多値を返す場合でも同様に省略できます。
func hoge() (n int, err error) {
n, err = fuga()
return
}
すごい!便利だ!
実は特に意味はない
最後に何か関数を呼んでその戻り値のerrがnilか判定するような関数なら、そもそもNamed Result Parametersを使わなくても簡潔に書けます。
func CanAtoI(s string) error {
_, err := strconv.Atoi(s)
return err
}
多値の場合も、だいたい関数の戻り値をとることが多いので、 これでもっと簡潔にかけます。
func hoge() (n int, err error) {
return fuga()
}
これのメリット
一応メリットがないわけではないです。
例えば、こういうような最後に呼び出す関数が副作用を持っていて、かつ引数にポインタで渡した変数が戻り値に含まれる場合はメリットがあります。
Named Result Parametersを使わない場合はこうですが、
func Count(db *sql.DB) (int, error) {
var count int
row := db.QueryRow("SELECT COUNT(*) FROM user")
err := row.Scan(&count)
if err != nil {
return count, err
}
return count, nil
}
Named Result Parametersを使うとcountの変数宣言も省けてかなり簡潔になります。これは結構有用。
func Count(db *sql.DB) (count int, err error) {
row := db.QueryRow("SELECT COUNT(*) FROM user")
err = row.Scan(&count)
return
}
こういったケースではNamed Result Parametersを使った方が簡潔に書けます。
でも、そもそもこんなケースは少ないので、やっぱり大したメリットではないです。
まとめ
最初Named Result Parametersで簡潔に書けることに気づいたときは、これはすごいことに気づいたぞ!と思って書き始めたら、書いてる途中に大して書きやすくなったわけではないことに気が付いてしまって悲しい。
それに、もはやif err != nil{}に慣れ親しんでしまった身としては、わざわざ省略して書くと読むときに混乱するし、パッと見はエラー処理していないように見えて精神衛生上良くないので、やっぱりこれからも従来通りのエラー処理を書いていくことになりそう。