[インデックス 14786] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)に対する変更です。具体的には、単項演算子&
(アドレス演算子)のオペランドとして複合リテラルを使用する際の規則について、その記述を明確化しています。特に、複合リテラルが括弧で囲まれている場合でも&
演算子のオペランドとして有効であることを追記し、例を追加しています。
コミット
commit 614b02d22a66cfcd9f5c9631246b4b529484e642
Author: Robert Griesemer <gri@golang.org>
Date: Wed Jan 2 18:11:49 2013 -0800
spec: clarify language about unary operator &
A composite literal may be parenthesized when
used as operand for the unary operator &.
R=rsc, iant, r, ken
CC=golang-dev
https://golang.org/cl/6996053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/614b02d22a66cfcd9f5c9631246b4b529484e642
元コミット内容
spec: clarify language about unary operator &
A composite literal may be parenthesized when
used as operand for the unary operator &.
R=rsc, iant, r, ken
CC=golang-dev
https://golang.org/cl/6996053
変更の背景
Go言語の仕様書において、単項演算子&
(アドレス演算子)がどのようなオペランドに対して適用できるかという記述は、元々「アドレス可能であること」を主要な要件としていました。しかし、複合リテラル(composite literal)は、その性質上、直接的にはアドレス可能ではありません。Go言語では、複合リテラルに&
を適用すると、そのリテラルが表す値がメモリ上に割り当てられ、そのアドレスが返されるという特別な挙動をします。
このコミットが行われた2013年1月時点では、Go言語はまだ比較的新しい言語であり、その仕様は継続的に洗練されていました。この変更の背景には、&
演算子と複合リテラルの組み合わせに関する既存の仕様記述が、開発者にとって曖昧であったり、誤解を招く可能性があったという認識があったと考えられます。特に、複合リテラルが括弧で囲まれている場合に&
演算子を適用できるかどうかが明示されていなかったため、この点に関する明確化が求められました。
この変更は、Go言語の仕様の正確性と網羅性を高め、開発者が言語の挙動をより正確に理解できるようにすることを目的としています。仕様の明確化は、コンパイラの実装者にとっても、言語の挙動を一貫して実装するための重要な指針となります。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念について理解しておく必要があります。
-
単項演算子
&
(アドレス演算子): Go言語における&
は、変数のメモリアドレスを取得するための演算子です。例えば、&x
は変数x
がメモリ上のどこに格納されているかを示すポインタを返します。通常、この演算子は「アドレス可能な」オペランドに対してのみ適用できます。 -
アドレス可能性 (Addressability): Go言語において「アドレス可能」とは、その値がメモリ上の特定の場所に格納されており、その場所を指すポインタを取得できることを意味します。変数、ポインタの逆参照、スライスインデックス操作、アドレス可能な構造体のフィールドセレクタ、アドレス可能な配列のインデックス操作などがアドレス可能です。リテラル(例:
10
,"hello"
,true
)は通常、アドレス可能ではありません。なぜなら、それらは一時的な値であり、特定のメモリ位置に永続的に関連付けられていないからです。 -
複合リテラル (Composite Literals): 複合リテラルは、構造体(struct)、配列(array)、スライス(slice)、マップ(map)などの複合型をその場で初期化するための構文です。例えば、
Point{2, 3}
はPoint
型の構造体を初期化する複合リテラルです。 複合リテラルは、それ自体はアドレス可能ではありません。しかし、Go言語の特別なルールとして、複合リテラルに&
演算子を適用すると、そのリテラルが表す値がメモリ上に割り当てられ、その値へのポインタが返されます。これは、new(Type)
で型をゼロ値で初期化してポインタを返すのと似ていますが、複合リテラルでは初期値を指定できる点が異なります。例:
type Point struct { X, Y int } p1 := Point{1, 2} // Point型の値 p2 := &Point{3, 4} // Point型の値がメモリに割り当てられ、そのポインタがp2に代入される
-
Go言語仕様 (Go Language Specification): Go言語の公式な挙動を定義する文書です。コンパイラの実装者や言語の厳密な挙動を知りたい開発者にとっての最終的な情報源となります。この仕様書は、Go言語の進化とともに更新されます。
技術的詳細
このコミットは、Go言語仕様書の「アドレス演算子」に関するセクション(Unary operators
の一部)を修正しています。
変更前の仕様では、&
演算子のオペランドは「アドレス可能」である必要があると記述されていました。しかし、例外として「複合リテラル」もオペランドになり得るとされていました。この例外は、複合リテラルが直接アドレス可能ではないにもかかわらず、&
を適用するとポインタが返されるというGo言語の特別な挙動を反映したものです。
このコミットによる変更は、この例外に関する記述をさらに明確にしています。具体的には、複合リテラルが「(possibly parenthesized)」(おそらく括弧で囲まれていても)&
演算子のオペランドになり得ることを明示的に追記しました。
なぜ「括弧で囲まれていても」という記述が重要なのでしょうか?
Go言語では、式を括弧で囲むことは、演算子の優先順位を明示したり、式のグループ化を行ったりするために一般的に行われます。例えば、&(Point{2, 3})
のように複合リテラルを括弧で囲んだ場合、これが&Point{2, 3}
と同じ意味を持つのか、あるいは構文エラーになるのかが、仕様上明確でなかった可能性があります。この変更により、括弧の有無にかかわらず、複合リテラルが&
演算子の有効なオペランドであることが保証され、開発者の混乱を防ぎます。
また、変更後の仕様では、&Point{2, 3}
という具体的な例が追加されています。これにより、複合リテラルに&
演算子を適用する典型的なユースケースが視覚的に示され、理解が深まります。
この変更は、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",
-\t"Subtitle": "Version of December 12, 2012",
+\t"Subtitle": "Version of January 2, 2013",
"Path": "/ref/spec"
}-->
@@ -3246,6 +3246,7 @@ that is, either a variable, pointer indirection, or slice indexing
operation; or a field selector of an addressable struct operand;
or an array indexing operation of an addressable array.
As an exception to the addressability requirement, <code>x</code> may also be a
+(possibly parenthesized)
<a href="#Composite_literals">composite literal</a>.
</p>
<p>
@@ -3259,6 +3260,7 @@ will cause a <a href="#Run_time_panics">run-time panic">run-time panic</a>.
<pre>
&x
&a[f(2)]
+&Point{2, 3}
*p
*pf(x)
</pre>
具体的な変更点は以下の通りです。
-
仕様書のバージョン日付の更新:
Subtitle
のバージョン日付が "Version of December 12, 2012" から "Version of January 2, 2013" に更新されています。これは、このコミットが2013年1月2日に行われたことを反映しています。 -
複合リテラルに関する記述の追加:
&
演算子のオペランドがアドレス可能であるという要件の例外として、複合リテラルが挙げられている箇所に、(possibly parenthesized)
という記述が追加されました。 変更前:As an exception to the addressability requirement, <code>x</code> may also be a <a href="#Composite_literals">composite literal</a>.
変更後:As an exception to the addressability requirement, <code>x</code> may also be a (possibly parenthesized) <a href="#Composite_literals">composite literal</a>.
-
例の追加:
&
演算子の使用例のリストに、&Point{2, 3}
という行が追加されました。これは、複合リテラルに&
演算子を適用する具体的な例を示しています。
コアとなるコードの解説
このコミットの「コアとなるコード」は、Go言語の仕様書そのものです。Go言語の仕様書は、言語の挙動を定義する最も重要なドキュメントであり、コンパイラやツールの実装、そして開発者のコード記述の指針となります。
変更されたHTMLコードは、Go言語仕様書の「単項演算子」セクションの一部です。
-
<a href="#Composite_literals">composite literal</a>
の直前に(possibly parenthesized)
が挿入されたことで、複合リテラルが括弧で囲まれていても&
演算子のオペランドとして有効であることが明示されました。これは、構文の柔軟性を示し、開発者が&(&Type{...})
のような形式を使用しても問題ないことを保証します。 -
<pre>
タグで囲まれたコード例のブロックに&Point{2, 3}
が追加されたことは、この仕様変更の意図を具体的に示すものです。Point{2, 3}
は構造体複合リテラルであり、これに&
を適用することで、Point
型のインスタンスがメモリに割り当てられ、そのポインタが生成されるというGo言語の慣用的なパターンを例示しています。この例は、仕様のテキスト記述を補完し、読者の理解を助けます。
この変更は、Go言語の構文やセマンティクスに新たな機能を追加するものではなく、既存の挙動に関する記述をより正確かつ網羅的にすることで、仕様の曖昧さを解消し、言語の理解を深めることを目的としています。
関連リンク
- Go言語仕様書: https://go.dev/ref/spec (このコミットが修正したドキュメントの最新版)
- Go言語の複合リテラルに関する公式ドキュメント: https://go.dev/ref/spec#Composite_literals
- Go言語の単項演算子に関する公式ドキュメント: https://go.dev/ref/spec#Unary_operators
参考にした情報源リンク
- Go言語の公式ドキュメントおよび仕様書
- Go言語のソースコードリポジトリ (GitHub)
- Go言語のコミット履歴と関連するコードレビュー (Gerrit CL 6996053)
- https://golang.org/cl/6996053 (コミットメッセージに記載されているGerritの変更リストへのリンク)
- Go言語におけるアドレス可能性とポインタに関する一般的な解説記事