Go の複数戻り値と名前付き戻り値
Go の関数は、複数の値を同時に返すことができる。多くの言語では戻り値は 1 つが基本だが、Go では最初から複数戻り値が言語仕様に組み込まれており、特にエラーハンドリングのパターンで欠かせない機能となっている。
複数戻り値の基本
関数の戻り値の型をカッコで囲み、カンマで区切ることで複数の値を返せる。
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("ゼロ除算はできません")
}
return a / b, nil
}呼び出し側では、戻り値の数だけ変数を用意して受け取る。
result, err := divide(10, 3)
if err != nil {
fmt.Println("エラー:", err)
return
}
fmt.Println(result) // 3.3333333333333335この 値, err というパターンは Go のコード全体で頻出する慣習であり、標準ライブラリでも広く採用されている。たとえば os.Open はファイルとエラーを、strconv.Atoi は変換結果とエラーをそれぞれ返す。
不要な戻り値の破棄
複数の戻り値のうち一部だけが必要な場合は、ブランク識別子 _ を使って不要な値を破棄できる。
// エラーだけチェックしたい場合
_, err := os.Create("test.txt")
// 値だけ欲しい場合(非推奨だがコンパイルは通る)
value, _ := strconv.Atoi("42")ただし、エラーを _ で破棄するのは基本的に避けるべきだ。エラーを無視すると、問題が発生したときに原因を特定しにくくなる。
名前付き戻り値
戻り値に名前を付けることもできる。名前付き戻り値は関数の先頭でゼロ値に初期化され、return 文で変数名を省略して返せる(ネイキッドリターン)。
func rectangleArea(width, height float64) (area float64, perimeter float64) {
area = width * height
perimeter = 2 * (width + height)
return // area と perimeter が自動的に返される
}名前付き戻り値にはドキュメントとしての役割もある。関数シグネチャを見ただけで、何が返されるのかが明確になるからだ。
func split(sum int) (int, int) のように、型だけでは何を返すのかわかりにくい
func split(sum int) (x, y int) のように、戻り値の意味が関数シグネチャから読み取れる
ネイキッドリターンの注意点
ネイキッドリターン(return だけで値を省略する書き方)は短い関数では便利だが、長い関数では可読性が落ちるため注意が必要になる。Go の公式ドキュメントでも、ネイキッドリターンは短い関数でのみ使うことが推奨されている。
// 短い関数 → ネイキッドリターンでも読みやすい
func half(n int) (result int, isEven bool) {
result = n / 2
isEven = n%2 == 0
return
}
// 長い関数 → 明示的に返す方がわかりやすい
func processData(data []byte) (result string, err error) {
// ... 数十行の処理 ...
return result, err // 明示的に書く
}実用的な活用パターン
複数戻り値は、マップのキー存在チェックでも活用される。
m := map[string]int{"apple": 100, "banana": 200}
price, ok := m["apple"]
if ok {
fmt.Println("apple の価格:", price)
}
// より短い書き方(if の初期化文)
if price, ok := m["cherry"]; ok {
fmt.Println("cherry の価格:", price)
} else {
fmt.Println("cherry は見つかりません")
}この value, ok パターンは「カンマ ok イディオム」と呼ばれ、マップのアクセスや型アサーション、チャネルの受信など Go のさまざまな場面で使われている。複数戻り値は単なる便利機能ではなく、Go のエラーハンドリングや安全なコーディングを支える根幹の仕組みといえる。