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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、およびランタイムの動作を定義する、開発者にとって最も重要なドキュメントの一つです。具体的には、可変長引数(variadic functions)の最終パラメータに渡される値に関する記述を明確にすることを目的としています。

コミット

commit a76627774272c7278819f3e5a9990c7a00183882
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Mar 6 10:35:05 2014 -0800

    spec: clarify value passed for final parameter of variadic functions
    
    NOT A LANGUAGE CHANGE.
    
    Fixes #7073.
    
    LGTM=r
    R=r, rsc, iant, ken
    CC=golang-codereviews
    https://golang.org/cl/68840045

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

https://github.com/golang/go/commit/a76627774272c7278819f3e5a9990c7a00183882

元コミット内容

spec: clarify value passed for final parameter of variadic functions

NOT A LANGUAGE CHANGE.

Fixes #7073.

LGTM=r
R=r, rsc, iant, ken
CC=golang-codereviews
https://golang.org/cl/68840045

変更の背景

このコミットは、Go言語の可変長引数関数の動作に関する仕様の記述を明確にすることを目的としています。特に、可変長引数パラメータに何も引数が渡されなかった場合に、そのパラメータが関数内でどのような値を持つのかについて、以前の仕様では曖昧な点がありました。

Goの可変長引数関数は、内部的にはスライスとして扱われます。例えば、func foo(args ...int) という関数があった場合、args は関数内で []int 型として扱われます。しかし、foo() のように引数を全く渡さなかった場合に、args が空のスライス([]int{})になるのか、それとも nil スライスになるのかが、仕様上明確ではありませんでした。

この曖昧さは、開発者が可変長引数パラメータの扱いを誤解する可能性があり、特に nil と空のスライスの違いが重要なGoのセマンティクスにおいて、混乱を招く可能性がありました。このコミットは、この点を明確にし、可変長引数パラメータに引数が渡されなかった場合には nil スライスが渡されることを明記することで、仕様の正確性と理解しやすさを向上させています。コミットメッセージに「NOT A LANGUAGE CHANGE.」とあるように、これは言語の動作そのものを変更するものではなく、既存の動作をより正確に記述するための仕様の修正です。

前提知識の解説

Go言語の可変長引数関数 (Variadic Functions)

Go言語では、関数の最後のパラメータに ... を付けることで、その関数が可変長引数を受け取れるようになります。これにより、関数呼び出し時に任意の数の引数を渡すことができます。

例:

func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

// 呼び出し例
sum(1, 2, 3)       // nums は []int{1, 2, 3}
sum(10)            // nums は []int{10}
sum()              // nums は何になる? (このコミットで明確化される点)

関数内で可変長引数パラメータは、その型のスライスとして扱われます。上記の例では、nums[]int 型になります。

Go言語のスライス (Slices)

スライスはGo言語の強力なデータ構造で、配列のセグメントを参照します。スライスは、長さ(len)と容量(cap)を持ちます。

スライスには、以下の2つの重要な状態があります。

  1. nil スライス:

    • 宣言されただけで初期化されていないスライス(例: var s []int)。
    • 基底配列を持たず、長さも容量も0です。
    • s == niltrue を返します。
    • len(s)cap(s) はどちらも 0 を返します。
    • nil スライスは、有効なゼロ値であり、多くのGoの関数やメソッドで「空」または「存在しない」コレクションを表すために使用されます。
  2. 空のスライス (Empty Slice):

    • make([]int, 0)[]int{} のように、長さ0で明示的に初期化されたスライス。
    • 基底配列を持つ場合と持たない場合がありますが、長さは常に0です。
    • s == nilfalse を返します(通常)。
    • len(s)cap(s) はどちらも 0 を返します。

nil スライスと空のスライスは、どちらも長さが0ですが、nil スライスは基底配列を持たないという点で異なります。この違いは、特にJSONのエンコード/デコードや、特定のライブラリの動作において重要になることがあります。

技術的詳細

このコミットは、Go言語仕様の「Passing arguments to ... parameters」セクションを修正し、可変長引数関数に引数が渡されなかった場合の動作を明確にしています。

変更前は、可変長引数パラメータは常に「新しいスライス」として扱われると記述されていました。しかし、引数が全く渡されないケースについて、その「新しいスライス」が nil スライスになるのか、それとも空のスライスになるのかが明示されていませんでした。

変更後の仕様では、以下の点が明確に記述されました。

  1. 可変長引数パラメータの型: ...T 型の最終パラメータ p は、関数内では []T 型として扱われる。これは変更されていません。
  2. 引数が渡されない場合: fp に対して実際の引数なしで呼び出された場合、p に渡される値は nil である。
  3. 引数が渡される場合: それ以外の場合(つまり、1つ以上の引数が渡された場合)、渡される値は新しいスライスであり、その基底配列の連続する要素が実際の引数となる。このスライスの長さと容量は、p にバインドされた引数の数となる。

この変更により、Greeting("nobody") のように可変長引数部分に何も渡さない呼び出しの場合、関数内の who パラメータは nil スライスになることが明確になりました。一方、Greeting("hello:", "Joe", "Anna", "Eileen") のように引数が渡された場合は、以前と同様に []string{"Joe", "Anna", "Eileen"} のような新しいスライスが生成されます。

この仕様の明確化は、Go言語の設計思想である「ゼロ値の有用性」とも一致しています。スライスのゼロ値は nil であり、これは「空」の状態を自然に表現します。可変長引数に引数がない場合も、そのゼロ値である nil スライスが渡されることで、より一貫性のある動作が保証されます。

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

--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -2915,27 +2915,32 @@ There is no distinct method type and there are no method literals.
 <h3 id="Passing_arguments_to_..._parameters">Passing arguments to <code>...</code> parameters</h3>
 
 <p>
-If <code>f</code> is variadic with final parameter type <code>...T</code>,
-then within the function the argument is equivalent to a parameter of type
-<code>[]T</code>.  At each call of <code>f</code>, the argument
-passed to the final parameter is
-a new slice of type <code>[]T</code> whose successive elements are
-the actual arguments, which all must be <a href="#Assignability">assignable</a>
-to the type <code>T</code>. The length of the slice is therefore the number of
-arguments bound to the final parameter and may differ for each call site.
+If <code>f</code> is <a href="#Function_types">variadic</a> with a final
+parameter <code>p</code> of type <code>...T</code>, then within <code>f</code>
+the type of <code>p</code> is equivalent to type <code>[]T</code>.
+If <code>f</code> is invoked with no actual arguments for <code>p</code>,
+the value passed to <code>p</code> is <code>nil</code>.
+Otherwise, the value passed is a new slice
+of type <code>[]T</code> with a new underlying array whose successive elements
+are the actual arguments, which all must be <a href="#Assignability">assignable</a>
+to <code>T</code>. The length and capacity of the slice is therefore
+the number of arguments bound to <code>p</code> and may differ for each
+call site.
 </p>
 
 <p>
-Given the function and call
+Given the function and calls
 </p>
 <pre>
  func Greeting(prefix string, who ...string)
+Greeting("nobody")
 Greeting("hello:", "Joe", "Anna", "Eileen")
 </pre>
 
 <p>
  within <code>Greeting</code>, <code>who</code> will have the value
-<code>[]string{"Joe", "Anna", "Eileen"}</code>
+<code>nil</code> in the first call, and
+<code>[]string{"Joe", "Anna", "Eileen"}</code> in the second.\n
 </p>
 
 <p>

コアとなるコードの解説

このコミットは、doc/go_spec.html 内の「Passing arguments to ... parameters」セクションの記述を修正しています。

変更前:

If <code>f</code> is variadic with final parameter type <code>...T</code>,
then within the function the argument is equivalent to a parameter of type
<code>[]T</code>.  At each call of <code>f</code>, the argument
passed to the final parameter is
a new slice of type <code>[]T</code> whose successive elements are
the actual arguments, which all must be <a href="#Assignability">assignable</a>
to the type <code>T</code>. The length of the slice is therefore the number of
arguments bound to the final parameter and may differ for each call site.

この記述では、「新しいスライス」が渡されるとされていますが、引数が全くない場合にそのスライスが nil なのか空なのかが不明確でした。

変更後:

If <code>f</code> is <a href="#Function_types">variadic</a> with a final
parameter <code>p</code> of type <code>...T</code>, then within <code>f</code>
the type of <code>p</code> is equivalent to type <code>[]T</code>.
If <code>f</code> is invoked with no actual arguments for <code>p</code>,
the value passed to <code>p</code> is <code>nil</code>.
Otherwise, the value passed is a new slice
of type <code>[]T</code> with a new underlying array whose successive elements
are the actual arguments, which all must be <a href="#Assignability">assignable</a>
to <code>T</code>. The length and capacity of the slice is therefore
the number of arguments bound to <code>p</code> and may differ for each
call site.

この修正では、以下の重要な行が追加されました。 If <code>f</code> is invoked with no actual arguments for <code>p</code>, the value passed to <code>p</code> is <code>nil</code>. これにより、可変長引数パラメータに引数が渡されなかった場合、そのパラメータには nil スライスが渡されることが明確に定義されました。

また、例のセクションも更新され、Greeting("nobody") の呼び出し例が追加されました。

変更前:

Given the function and call
</p>
<pre>
 func Greeting(prefix string, who ...string)
Greeting("hello:", "Joe", "Anna", "Eileen")
</pre>

<p>
 within <code>Greeting</code>, <code>who</code> will have the value
<code>[]string{"Joe", "Anna", "Eileen"}</code>

変更後:

Given the function and calls
</p>
<pre>
 func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
</pre>

<p>
 within <code>Greeting</code>, <code>who</code> will have the value
<code>nil</code> in the first call, and
<code>[]string{"Joe", "Anna", "Eileen"}</code> in the second.
</p>

この例の追加により、Greeting("nobody") の場合 whonil になること、そして Greeting("hello:", "Joe", "Anna", "Eileen") の場合 who[]string{"Joe", "Anna", "Eileen"} になることが、具体的なコード例とともに示され、仕様の理解がさらに深まりました。

関連リンク

参考にした情報源リンク