[インデックス 18761] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)に、シャドーイングされた戻り値パラメータに関する実装上の制限を追記するものです。具体的には、名前付き戻り値パラメータがシャドーイングされている場合に、空のreturn
文を許可しないというコンパイラの挙動を明文化しています。これは言語仕様の変更ではなく、既存のコンパイラ(gcおよびgccgo)の挙動を文書化したものです。
コミット
commit c97778f4302bd0e39045c931941e32f178493f45
Author: Robert Griesemer <gri@golang.org>
Date: Wed Mar 5 11:59:53 2014 -0800
spec: shadowed return parameters may be disallowed
This documents the implemented behavior of both
gc and gccgo as an implementation restriction.
NOT A LANGUAGE CHANGE.
Fixes #5425.
LGTM=rsc, r, iant
R=r, iant, rsc, ken
CC=golang-codereviews
https://golang.org/cl/71430043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c97778f4302bd0e39045c931941e32f178493f45
元コミット内容
spec: shadowed return parameters may be disallowed
This documents the implemented behavior of both
gc and gccgo as an implementation restriction.
NOT A LANGUAGE CHANGE.
Fixes #5425.
変更の背景
Go言語では、関数が名前付き戻り値パラメータを持つことができます。これらのパラメータは、関数の冒頭でゼロ値で初期化され、return
文で明示的に値を指定しない場合、その時点での値が返されます。しかし、関数内部でこれらの名前付き戻り値パラメータと同じ名前の新しい変数を:=
(短い変数宣言)を使って宣言すると、その新しい変数が元の戻り値パラメータを「シャドーイング(shadowing)」してしまいます。
シャドーイングされた変数は、そのスコープ内では元の変数とは異なる独立した存在となります。このため、シャドーイングされた変数に値を代入しても、元の名前付き戻り値パラメータの値は変更されません。特に、エラーハンドリングで頻繁に用いられるerr
変数がシャドーイングされると、開発者が意図しない挙動(例えば、関数が常にnil
エラーを返すなど)を引き起こす可能性がありました。
この問題は、Goコミュニティで認識されており、Issue #5425として報告されていました。このコミットは、このような混乱を避けるため、コンパイラがシャドーイングされた戻り値パラメータが存在するスコープで空のreturn
文(return
のみで戻り値を指定しない形式)を許可しないという、既存のコンパイラ(gcおよびgccgo)の挙動をGo言語の仕様書に明文化することを目的としています。これにより、開発者はシャドーイングによる潜在的な問題をより明確に認識し、適切なコーディングスタイルを適用するよう促されます。
前提知識の解説
1. Go言語の名前付き戻り値パラメータ (Named Return Parameters)
Go言語の関数は、戻り値に名前を付けることができます。例えば、func myFunc() (result int, err error)
のように宣言すると、result
とerr
がその関数の戻り値パラメータとして定義されます。これらのパラメータは、関数の本体に入ると自動的にその型のゼロ値(int
なら0
、error
ならnil
)で初期化されます。関数内でreturn
文が実行される際、明示的に戻り値を指定しないreturn
(裸のreturn
)を使用すると、これらの名前付き戻り値パラメータの現在の値がそのまま関数の戻り値となります。
func divide(a, b int) (quotient int, err error) {
if b == 0 {
err = errors.New("division by zero") // err は名前付き戻り値パラメータ
return // err の現在の値 (errors.New("division by zero")) が返される
}
quotient = a / b // quotient は名前付き戻り値パラメータ
return // quotient の現在の値 (a/b) が返される
}
2. 変数のシャドーイング (Variable Shadowing)
シャドーイングとは、内側のスコープで宣言された変数が、外側のスコープで同じ名前を持つ変数を「隠す」現象を指します。Go言語では、特に短い変数宣言:=
を使用する際にシャドーイングが発生しやすいです。
package main
import "fmt"
func main() {
x := 10 // 外側のスコープの x
if true {
x := 20 // 内側のスコープの x。外側の x をシャドーイング
fmt.Println(x) // 20 を出力
}
fmt.Println(x) // 10 を出力
}
この例では、if
ブロック内でx := 20
とすることで、新しいx
変数が宣言され、外側のx
をシャドーイングしています。if
ブロック内のx
への変更は、外側のx
には影響しません。
3. :=
と =
の違い
:=
(短い変数宣言): 変数の宣言と初期化を同時に行います。左辺に少なくとも1つの新しい変数が含まれている必要があります。新しい変数を宣言するため、シャドーイングを引き起こす可能性があります。=
(代入): 既に宣言されている変数に値を代入します。新しい変数を宣言することはありません。
シャドーイングの問題は、名前付き戻り値パラメータと同じ名前の変数を:=
で宣言してしまうことで発生します。これにより、開発者が意図していた名前付き戻り値パラメータではなく、シャドーイングされた新しいローカル変数に値が代入されてしまい、裸のreturn
文が期待通りの値を返さないというバグにつながることがあります。
技術的詳細
このコミットは、Go言語の仕様書(doc/go_spec.html
)の「Return statements」セクションに新しい段落を追加することで、シャドーイングされた戻り値パラメータに関するコンパイラの実装上の制限を明文化しています。
追加された内容は以下の通りです。
Implementation restriction: A compiler may disallow an empty expression list in a "return" statement if a different entity (constant, type, or variable) with the same name as a result parameter is in scope at the place of the return.
これは、「コンパイラは、戻り値パラメータと同じ名前の異なるエンティティ(定数、型、または変数)がreturn
文の場所でスコープ内にある場合、空の式リストを持つreturn
文を許可しないことがある」という意味です。
重要な点は、これが「Implementation restriction(実装上の制限)」であり、「NOT A LANGUAGE CHANGE(言語の変更ではない)」と明記されていることです。これは、Go言語の文法やセマンティクス自体を変更するものではなく、既存のGoコンパイラ(gcやgccgoなど)が既にこの挙動を実装していることを仕様書に追記し、その挙動を正式に文書化したものです。
この制限の目的は、前述のシャドーイングによる混乱やバグを防ぐことにあります。特に、名前付き戻り値パラメータがシャドーイングされている状況で、開発者が意図せず裸のreturn
文を使用してしまい、期待と異なる値が返されることを防ぐためのガードレールとして機能します。コンパイラがこのようなケースをエラーとして扱うことで、開発者は早期に問題を検出し、=
による明示的な代入を使用するなど、正しい方法でコードを修正するよう促されます。
コミットに含まれる例は、この制限が適用される典型的なシナリオを示しています。
func f(n int) (res int, err error) {
if _, err := f(n-1); err != nil {
return // invalid return statement: err is shadowed
}
return
}
この例では、f
関数はres
とerr
という名前付き戻り値パラメータを持っています。if
文の条件部で_, err := f(n-1)
という短い変数宣言が使われています。ここで宣言されたerr
は、関数の戻り値パラメータであるerr
をシャドーイングしています。このシャドーイングされたerr
がnil
でない場合にreturn
(裸のreturn
)が実行されると、コンパイラはこのreturn
文を無効と判断します。なぜなら、このreturn
文の場所では、名前付き戻り値パラメータのerr
がシャドーイングされており、裸のreturn
がどのerr
を返すのか曖昧になるためです。
この変更は、Go言語の堅牢性を高め、開発者がより安全で予測可能なコードを書くのを助けるためのものです。
コアとなるコードの変更箇所
変更はdoc/go_spec.html
ファイルに対して行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of March 4, 2014",
+ "Subtitle": "Version of March 5, 2014",
"Path": "/ref/spec"
}-->
@@ -5002,6 +5002,21 @@ function. A "return" statement that specifies results sets the result parameters
any deferred functions are executed.
</p>
+<p>
+Implementation restriction: A compiler may disallow an empty expression list
+in a "return" statement if a different entity (constant, type, or variable)
+with the same name as a result parameter is in
+<a href="#Declarations_and_scope">scope</a> at the place of the return.
+</p>
+
+<pre>
+func f(n int) (res int, err error) {
+\tif _, err := f(n-1); err != nil {\n+\t\treturn // invalid return statement: err is shadowed
+\t}\n+\treturn
+}\n+</pre>
+
<h3 id="Break_statements">Break statements</h3>
コアとなるコードの解説
このコミットの主要な変更は、doc/go_spec.html
に新しい<p>
タグと<pre>
タグを追加したことです。
-
<p>
タグで囲まれた説明文の追加:<p> Implementation restriction: A compiler may disallow an empty expression list in a "return" statement if a different entity (constant, type, or variable) with the same name as a result parameter is in <a href="#Declarations_and_scope">scope</a> at the place of the return. </p>
この段落は、Goコンパイラがどのようにシャドーイングされた戻り値パラメータを扱うかについて、実装上の制限を記述しています。具体的には、戻り値パラメータと同じ名前の別のエンティティ(定数、型、または変数)が
return
文のスコープ内に存在する場合、コンパイラは空の式リストを持つreturn
文(つまり、裸のreturn
)を許可しない可能性があることを述べています。これは、コンパイラがそのような状況をエラーとして扱うことを意味し、開発者にシャドーイングの問題を認識させ、修正を促します。 -
<pre>
タグで囲まれたコード例の追加:<pre> func f(n int) (res int, err error) { if _, err := f(n-1); err != nil { return // invalid return statement: err is shadowed } return } </pre>
このコード例は、上記の制限が適用される具体的なシナリオを示しています。
f
関数はres
とerr
という名前付き戻り値パラメータを持っています。if _, err := f(n-1);
の行では、新しいerr
変数が宣言され、関数の名前付き戻り値パラメータであるerr
をシャドーイングしています。このシャドーイングされたerr
がnil
でない場合に実行されるreturn
文は、コメントで示されているように「invalid return statement: err is shadowed」と見なされます。これは、裸のreturn
がどのerr
(シャドーイングされたローカル変数か、名前付き戻り値パラメータか)を参照すべきか曖昧になるため、コンパイラがエラーを発生させることを意味します。
これらの変更は、Go言語の仕様書をより正確かつ詳細にし、開発者がシャドーイングによる潜在的な落とし穴を理解し、回避するための重要な情報を提供します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/c97778f4302bd0e39045c931941e32f178493f45
- Go Issue 5425 (関連する可能性のある情報): https://github.com/golang/go/issues/5425 (ただし、検索結果では直接的なIssue 5425の詳細は見つかりませんでした。一般的なシャドーイングの問題として扱われているようです。)
参考にした情報源リンク
- Stack Overflow: Go variable shadowing and named return values (https://stackoverflow.com/questions/tagged/go-variable-shadowing)
- Reddit: Go variable shadowing discussions (https://www.reddit.com/r/golang/comments/...)
- rpeshkov.net: Understanding Go's variable shadowing (https://rpeshkov.net/go-variable-shadowing/)
- Go Language Specification: Return statements (https://go.dev/ref/spec#Return_statements) (コミットが適用された後の最新版の仕様書)