複数の値を返す
結果の型をかっこの中に並べます。値はカンマで区切って返し、同じように受け取ります。
組み込みの min と max はGo 1.21からあります。呼び出し側はすべての結果を受け取る必要があります。x := minMax(nums) は assignment mismatch: 1 variable but minMax returns 2 values でコンパイルに失敗します。
(value, error)の慣習
2つの値を返す最もよくある理由は、失敗を報告するためです。慣習としてエラーは最後に置き、それがnilでないときは他の結果に意味がないものとして扱います。
出力:
port: 8080
error: port "http": strconv.Atoi: parsing "http": invalid syntax
error: port out of range
失敗したときは、他のすべての結果にゼロ値(0、""、nil)を返します。ラップやチェックを含むパターンの全体はエラー処理のページにあります。
カンマokイディオム
いくつかの組み込みの操作は、1つ目の値が本物かどうかを示す bool を省略可能な2つ目の値として返します。この変数には慣習的に ok という名前を付けます。
この3つの形(マップの参照、型アサーション、チャネルからの受信)は特別で、代入する変数の数によって1つか2つの値を返します。自分で書いた関数ではこれはできません。2つの結果を宣言した関数は、常に2つ返します。
ブランク識別子
_ は値を受け取って捨てます。不要な結果に使います。
_, err := fmt.Sscan(input, &n)
key, _ := parseLine(line)
_ でエラーを無視してもコンパイルは通るので、呼び出しが失敗しえない、または失敗しても問題ないとわかっているときだけにしてください。たとえば strings.Builder.WriteString は常にnilのエラーを返しますが、書き込み後のファイルのクローズはそうではありません。
名前付き戻り値
結果パラメータには名前を付けられます。関数の先頭で宣言され、ゼロ値で初期化され、値のない return はそれらが保持しているものを返します。
名前は go doc の出力に表示されるので、2つの結果が同じ型のときに役立ちます。(quotient, remainder int) は (int, int) より多くを語ります。
長い関数での値のない return は読みにくくなります。読む人は結果が何を保持しているかを確かめるために、さかのぼって読まなければならないからです。よくある助言は、名前がドキュメントになるときは結果に名前を付け、それでも関数が数行より長いなら return quotient, remainder と明示的に書くことです。
名前付き戻り値とdefer
遅延実行される関数は、return 文が結果を設定した後、呼び出し側がそれを受け取る前に実行されます。名前付き戻り値があると、遅延実行される関数がそれを読んだり変更したりできます。関数が返すあらゆるエラーに文脈を加えたり、panicをエラーに変えたりするのはこの方法です。
これは load : empty name と表示し、次に <nil> と表示します。return errors.New(...) はまず err に代入し、その後で遅延実行される関数がそれをラップします。詳しくはdeferのページを参照してください。
名前付き戻り値のシャドーイング
名前付き戻り値は、関数の最も外側のスコープにあります。ネストしたブロックの中で := を使うと同じ名前の新しい変数が作られ、値のない return は外側のほうを返します。
func find() (n int, err error) {
if true {
n, err := compute() // new n and err, shadowing the results
_ = n
_ = err
}
return // returns 0, nil
}
コンパイラは最悪のケースを検出します。結果がシャドーイングされているブロックの中で値のない return を書くと、result parameter err not in scope at return で失敗します。しかし、returnがブロックの外にある上のようなコードは検出しません。結果の変数を意図しているなら、:= ではなく = を使います。
よくある間違い
- errがnilでないのに値を使う。 先に
errをチェックします。関数はエラーと一緒にでたらめな値を返しても構わないのです。 - 型付きのnilポインタをエラーとして返す。
var e *MyErr; return eはnilでないerrorを返します。成功時はリテラルのnilを返します。 - 結果が多すぎる。 意味の異なる3つ以上の結果は使いにくくなります。代わりに構造体を返します。
よくある質問
Goで関数から複数の値を返すには?
結果の型をかっこの中に並べ、値をカンマで区切って返します:func minMax(xs []int) (int, int) { ...; return lo, hi }。呼び出し側はすべてを一度に代入します:lo, hi := minMax(nums)。
Goで戻り値の1つを無視するには?
ブランク識別子 _ に代入します:_, err := strconv.Atoi(s) や n, _ := strconv.Atoi(s)。値をまるごと省くことはできず、左辺の変数の数は結果の数と一致していなければなりません。
Goの名前付き戻り値とは何ですか?
func split(n int) (half, rest int) のように名前を持つ結果パラメータです。ゼロ値から始まり、本体で代入でき、値のない return はそれらの現在の値を返します。遅延実行される関数が、呼び出し側が受け取る値を変更できるようにもなります。
Goで裸のreturnを使うべきですか?
短い関数でだけ使います。長い関数での値のない return は何を返しているのかを隠し、シャドーイングのバグを生みやすくします。Goのスタイルガイドは、ドキュメントとして結果に名前を付けつつ、数行より長い関数では明示的に値を返すことを勧めています。