Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 15186] ファイルの概要

このコミットは、Go言語の公式仕様書 (doc/go_spec.html) における関数呼び出しの特殊なケースに関する記述を明確にするものです。具体的には、f(g()) の形式で関数 g の戻り値が直接関数 f の引数として渡される場合に、g が少なくとも1つの戻り値を返す必要があるという条件を追記しています。これにより、仕様の曖昧さを解消し、より厳密な定義を提供しています。

コミット

  • コミットハッシュ: 1b3083e68d2c9b93fe6ecaa1758dbf5e214e6784
  • Author: Russ Cox rsc@golang.org
  • Date: Sat Feb 9 14:46:55 2013 -0500

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/1b3083e68d2c9b93fe6ecaa1758dbf5e214e6784

元コミット内容

spec: clarify that f(g()) requires that g return >= 1 value

Fixes #4573.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/7322043

変更の背景

Go言語では、関数が複数の戻り値を返すことができ、その戻り値を別の関数の引数として直接渡すことができる特殊なケースが存在します。例えば、f(g()) のように記述することで、g() の戻り値が f() の引数に直接バインドされます。

このコミットが行われた2013年当時、Go言語の仕様書にはこの特殊なケースに関する記述がありましたが、g() が全く戻り値を返さない(つまり、戻り値の数が0である)場合にどうなるかについての明確な規定がありませんでした。この曖昧さが、コンパイラの実装や開発者の理解において混乱を招く可能性がありました。

このコミットは、Go言語のIssue #4573 を修正するために行われました。このIssueは、まさにこの曖昧さを指摘し、仕様の明確化を求めていたものと考えられます。Russ Cox氏(Go言語の主要な設計者の一人)によるこの変更は、言語仕様の厳密性を高め、将来的な実装の一貫性を保証することを目的としています。

前提知識の解説

Go言語の多値返却 (Multiple Return Values)

Go言語の大きな特徴の一つは、関数が複数の値を返すことができる点です。例えば、エラーハンドリングにおいて、value, err := someFunction() のように、結果とエラーを同時に返すパターンが広く用いられています。

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide by zero")
    }
    return a / b, nil
}

Go言語の関数呼び出しにおける特殊なケース

Go言語の関数呼び出しには、以下のような特殊なルールが存在します。

  1. 多値返却の直接引数渡し: ある関数 g が複数の値を返し、その戻り値の数と型が別の関数 f の引数の数と型に一致する場合、f(g(args_of_g)) の形式で g の戻り値を直接 f の引数として渡すことができます。これは、f の引数リストに g の呼び出し以外が含まれていない場合にのみ適用されます。

    例:

    func getCoords() (int, int) {
        return 10, 20
    }
    
    func printCoords(x, y int) {
        fmt.Printf("X: %d, Y: %d\n", x, y)
    }
    
    // この特殊なケースが適用される
    printCoords(getCoords()) // X: 10, Y: 20
    
  2. 可変長引数 (Variadic Parameters): 関数が ...T の形式で可変長引数を受け取る場合、スライスを ... を付けて渡すことで、スライスの要素を個別の引数として展開して渡すことができます。このコミットの変更箇所にも、可変長引数に関する記述が含まれていますが、今回の主要な変更点ではありません。

Go言語の仕様書 (Go Language Specification)

Go言語の仕様書は、Go言語の構文、セマンティクス、標準ライブラリの動作などを厳密に定義した公式文書です。Go言語のコンパイラやツール、そして開発者は、この仕様書に基づいて動作します。仕様書は、言語の挙動に関する最終的な権威であり、曖昧さがないように記述されることが理想とされます。

技術的詳細

このコミットの技術的な核心は、Go言語の仕様書における「関数呼び出しの特殊なケース」の記述に、g が少なくとも1つの戻り値を返す必要があるという制約を追加した点です。

変更前の仕様では、f(g(parameters_of_g)) の形式で g の戻り値が f の引数にバインドされる条件として、「g の戻り値の数と型が f の引数に一致すること」が挙げられていました。しかし、もし g が全く戻り値を返さない(つまり、戻り値の数が0)場合、このルールがどのように適用されるかが不明確でした。

例えば、以下のようなケースを考えます。

func noReturn() {
    // 何も返さない
}

func takesOneArg(x int) {
    fmt.Println(x)
}

// 変更前の仕様では曖昧だった可能性のある呼び出し
// takesOneArg(noReturn())

noReturn() は何も返しません。takesOneArg() は1つの int 型の引数を期待します。変更前の仕様の記述だけでは、noReturn() の戻り値(0個)が takesOneArg() の引数(1個)に「一致する」と解釈されるべきかどうかが曖昧でした。もし一致すると解釈された場合、takesOneArg には何が渡されるのか、あるいはコンパイルエラーになるのか、実行時パニックになるのか、といった挙動が不明確でした。

このコミットによって、g が少なくとも1つの戻り値を返す必要があるという条件が明示的に追加されたことで、上記のような takesOneArg(noReturn()) のような呼び出しは、この特殊なケースの対象外となり、コンパイルエラーとなることが明確になります。これにより、Go言語のコンパイラはより一貫性のあるエラーチェックを行うことができ、開発者はより予測可能なコードを書くことができるようになります。

この変更は、Go言語の設計哲学である「明確さ」と「厳密さ」を反映したものであり、言語の安定性と信頼性を向上させる上で重要な意味を持ちます。

コアとなるコードの変更箇所

変更は doc/go_spec.html ファイルの以下の部分です。

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -2765,13 +2765,14 @@ causes a <a href="#Run_time_panics">run-time panic</a>.
 </p>
 
 <p>
-As a special case, if the return parameters of a function or method
+As a special case, if the return values of a function or method
 <code>g</code> are equal in number and individually
 assignable to the parameters of another function or method
 <code>f</code>, then the call <code>f(g(<i>parameters_of_g</i>))</code>
 will invoke <code>f</code> after binding the return values of
 <code>g</code> to the parameters of <code>f</code> in order.  The call
-of <code>f</code> must contain no parameters other than the call of <code>g</code>.\n+of <code>f</code> must contain no parameters other than the call of <code>g</code>,\n+and <code>g</code> must have at least one return value.\n If <code>f</code> has a final <code>...</code> parameter, it is\n assigned the return values of <code>g</code> that remain after\n assignment of regular parameters.\n```

具体的には、以下の行が追加されました。

`and <code>g</code> must have at least one return value.`

また、細かな変更として、`return parameters` が `return values` に修正されていますが、これは意味的な変更ではなく、より適切な用語への修正と考えられます。

## コアとなるコードの解説

追加された `and <code>g</code> must have at least one return value.` という一文が、このコミットの最も重要な変更点です。

この一文は、`f(g(parameters_of_g))` という形式の関数呼び出しにおいて、関数 `g` が少なくとも1つの戻り値を返す必要があるという、明確な制約を課しています。

これにより、以下のような状況が明確に定義されます。

*   **`g` が1つ以上の戻り値を返す場合**: 以前と同様に、`g` の戻り値が `f` の引数にバインドされます。この特殊なケースが適用されます。
*   **`g` が0個の戻り値を返す場合**: この新しい制約により、`g` が少なくとも1つの戻り値を返さないため、`f(g())` の形式で `g` の戻り値を直接 `f` の引数として渡す特殊なケースは適用されません。結果として、このようなコードはコンパイルエラーとなるか、あるいは通常の関数呼び出しルールに従って処理されることになります(この場合は引数不足でコンパイルエラーになる可能性が高い)。

この変更は、Go言語の仕様の厳密性を高め、コンパイラの実装者やGo言語のユーザーが、特定の関数呼び出しの挙動について曖昧さなく理解できるようにするために不可欠です。

## 関連リンク

*   **GitHubコミットページ**: [https://github.com/golang/go/commit/1b3083e68d2c9b93fe6ecaa1758dbf5e214e6784](https://github.com/golang/go/commit/1b3083e68d2c9b93fe6ecaa1758dbf5e214e6784)
*   **Gerrit Change-ID**: [https://golang.org/cl/7322043](https://golang.org/cl/7322043) (Goプロジェクトでコードレビューに使われるGerritの変更セットへのリンク)

## 参考にした情報源リンク

*   Go言語の公式仕様書 (このコミットによって変更された文書自体)
*   Go言語の多値返却に関する一般的なドキュメントやチュートリアル
*   Go言語のIssueトラッカー (Issue #4573 の詳細が確認できればより詳細な情報が得られたが、今回は直接的な情報は見つからなかった)
*   Go言語の設計に関するRuss Cox氏の講演や記事 (言語設計の背景を理解するため)