[インデックス 1863] ファイルの概要
このコミットは、Go言語の初期の仕様書である doc/go_spec.html
に対する変更です。主にポインタの「アドレス演算子」に関する説明の追加と、HTMLのクリーンアップ、そしてメソッド値に関する記述の調整が含まれています。
コミット
commit afee1c5f0c1907bc575e1f87fc18889f0979cab0
Author: Rob Pike <r@golang.org>
Date: Fri Mar 20 17:41:25 2009 -0700
add simple text about & and *.
clean up html: PLEASE RUN TIDY WHEN YOU EDIT THIS DOCUMENT
deferring method value update until we decide what happens.
R=gri
DELTA=50 (38 added, 4 deleted, 8 changed)
OCL=26609
CL=26612
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/afee1c5f0c1907bc575e1f87fc18889f0979cab0
元コミット内容
add simple text about & and *.
clean up html: PLEASE RUN TIDY WHEN YOU EDIT THIS DOCUMENT
deferring method value update until we decide what happens.
R=gri
DELTA=50 (38 added, 4 deleted, 8 changed)
OCL=26609
CL=26612
変更の背景
このコミットは、Go言語がまだ開発の初期段階にあった2009年3月に行われたものです。当時のGo言語の仕様書 doc/go_spec.html
は、言語の機能が固まっていく中で頻繁に更新されていました。
このコミットの主な背景は以下の通りです。
- ポインタ演算子の説明の明確化: Go言語におけるアドレス演算子 (
&
) とポインタ間接演算子 (*
) は、C言語などから影響を受けた重要な概念ですが、その初期の仕様書では十分に説明されていませんでした。このコミットでは、これらの演算子の基本的な動作について簡潔な説明を追加し、仕様の明確化を図っています。 - HTMLの整形とクリーンアップ:
doc/go_spec.html
はHTML形式で記述されており、手動での編集によってHTMLの構造が乱れる可能性がありました。コミットメッセージにある「PLEASE RUN TIDY WHEN YOU EDIT THIS DOCUMENT」という指示は、HTML Tidyのようなツールを使用して、HTMLの整形と検証を行うことの重要性を示唆しています。これは、ドキュメントの可読性と保守性を高めるための一般的なプラクティスです。 - メソッド値の仕様の検討: Go言語の「メソッド値 (method value)」は、オブジェクトのメソッドを関数として扱うことができる強力な機能です。しかし、その初期の設計においては、
T.M
(型からのメソッド参照) とt.M
(インスタンスからのメソッド参照) のどちらを許可するか、あるいはその挙動をどう定義するかについて、まだ議論が進行中でした。このコミットでは、その決定を「deferring (延期する)」と明記しつつ、既存の記述を整理しています。これは、言語設計の複雑な側面であり、慎重な検討が必要であったことを示しています。 - TODO項目の整理: 仕様書には、将来的に対応すべき課題や検討事項が「TODO」として記載されていました。このコミットでは、
convert()
やtypeof
といった、後にGo言語の標準機能としては採用されなかった(あるいは異なる形で実装された)関数に関するTODO項目を削除し、仕様書の整理を進めています。
全体として、このコミットはGo言語の仕様をより正確で、読みやすく、そして将来の変更に備えるための継続的な努力の一環として行われました。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念とHTMLの知識が必要です。
1. Go言語のポインタとアドレス演算子 (&
)、ポインタ間接演算子 (*
)
Go言語におけるポインタは、変数のメモリアドレスを指し示す変数です。C/C++のポインタと似ていますが、ポインタ演算(アドレスの加減算)は許可されておらず、より安全に設計されています。
-
アドレス演算子 (
&
):- 変数の前に
&
を付けると、その変数のメモリアドレス(ポインタ)を取得できます。 - 例:
p := &x
は、変数x
のアドレスをポインタ変数p
に代入します。p
の型は*T
(T型へのポインタ) となります。 - このコミットでは、「
&
はオペランドのアドレスを生成する」と説明されています。オペランドは変数、ポインタ間接、フィールドセレクタ、配列またはスライスインデックス操作である必要があります。関数結果変数のアドレスを取ることは不正であると明記されています。
- 変数の前に
-
ポインタ間接演算子 (
*
) (デリファレンス演算子):- ポインタ変数の前に
*
を付けると、そのポインタが指し示すメモリアドレスに格納されている「値」を取得できます。 - 例:
v := *p
は、ポインタp
が指すアドレスに格納されている値をv
に代入します。 - このコミットでは、「
*
はオペランドが指し示す値を取得する」と説明されています。
- ポインタ変数の前に
2. Go言語のメソッドとメソッド値 (Method Values)
Go言語では、構造体などの型に「メソッド」を定義できます。メソッドは、特定の型に関連付けられた関数です。
-
メソッド:
type MyStruct struct { Value int } func (m MyStruct) GetValue() int { // MyStruct 型のメソッド return m.Value }
-
メソッド値 (Method Value):
- Go言語では、メソッドを「関数値」として取得し、変数に代入したり、別の関数に渡したりすることができます。これを「メソッド値」と呼びます。
- メソッド値は、レシーバ(メソッドが呼び出されるインスタンス)がバインドされた関数です。
- 例:
ms := MyStruct{Value: 10} f := ms.GetValue // メソッド値を取得。f は func() int 型の関数 fmt.Println(f()) // 10 を出力
- このコミットの時点では、
t.M
のような式がメソッド値としてどのように振る舞うか、特にfunc (t *T, a int) int
のような関数型に「デモート (demote)」されるという概念が説明されています。また、インターフェース型の場合のメソッド値の挙動についても言及されています。
3. HTMLの基本的な構造と整形
doc/go_spec.html
はHTMLファイルであるため、HTMLのタグや構造に関する知識が必要です。
- HTMLタグ:
<p>
(段落),<h3>
(見出し),<pre>
(整形済みテキスト),<code>
(コード) など。 - HTML Tidy: HTMLドキュメントの構文エラーをチェックし、整形(インデント、タグの閉じ忘れ修正など)を行うツール。コミットメッセージで言及されている「TIDY」は、このようなツールを指します。
4. Go言語の初期開発プロセス
Go言語は、Google社内でRob Pike、Ken Thompson、Robert Griesemerらによって設計・開発されました。初期の仕様策定は、試行錯誤と議論を重ねながら進められました。このコミットに見られる「TODO」コメントや「deferring (延期する)」といった記述は、その開発プロセスの痕跡です。
TODO
コメント: 仕様書やコード内に残された、将来的に対応が必要な項目。[x]
マーク: 完了したTODO項目を示すマーク。R=gri
: コードレビューの承認者を示す慣習。gri
はRobert Griesemerを指します。OCL
/CL
: 内部の変更リスト (Change List) 番号。
これらの前提知識を理解することで、コミットがGo言語の仕様の進化においてどのような意味を持つのか、より深く把握できます。
技術的詳細
このコミット afee1c5f0c1907bc575e1f87fc18889f0979cab0
は、Go言語の仕様書 doc/go_spec.html
に対して、主に以下の技術的な変更を加えています。
-
アドレス演算子 (
&
) とポインタ間接演算子 (*
) の説明の追加:- 以前は「TODO: This section is a mess. Skipping it for now.」や「TODO: Need to talk about unary "*", clean up section below.」と書かれていた「Address operators」セクションに、具体的な説明が追加されました。
&
演算子について、「オペランドのアドレスを生成する」こと、オペランドが変数、ポインタ間接、フィールドセレクタ、配列またはスライスインデックス操作である必要があること、そして関数結果変数のアドレスを取ることが不正であると明記されました。*
演算子について、「オペランドが指し示す値を取得する」と説明されました。- これらの演算子の使用例として、
&x
,&a[f(2)]
,*p
,*pf(x)
といったコードスニペットが<pre>
タグで追加されました。
-
HTML構造のクリーンアップと整形:
- コミットメッセージにもあるように、HTMLの整形が行われています。具体的には、
<p>
タグの追加や、既存のテキストを<p>
タグで囲むことで、段落構造を明確にしています。これにより、ドキュメントの可読性が向上し、HTMLのセマンティクスが改善されています。 - 例えば、メソッド値に関する記述の各ブロックが
<p>
タグで囲まれるようになっています。
- コミットメッセージにもあるように、HTMLの整形が行われています。具体的には、
-
メソッド値に関する記述の調整とTODOの追加:
- メソッド値に関する既存の記述は維持されつつ、HTMLの整形が施されています。
- 特に、
t.M
がfunc (t *T, a int) int
のような関数型に「デモート」されるという概念が強調されています。 - インターフェース型におけるメソッド値の挙動についても、
i.M
が自動的に生成される関数リテラルとして振る舞うことが説明されています。 - 新しいTODOコメントとして、「TODO: should probably describe the effect of (t.m) under §Expressions if t.m denotes a method: Effect is as described above, converts into function.」が追加され、メソッド値の挙動を「式」のセクションでより詳細に記述する必要があることが示唆されています。
- また、「TODO: Document implementation restriction: It is illegal to take the address of a result parameter (e.g.: func f() (x int, p *int) { return 2, &x }). (TBD: is it an implementation restriction or fact?)」というTODOも追加され、関数から返される結果パラメータのアドレスを取ることの制限について検討が必要であることが示されています。これは、Go言語のメモリモデルやガベージコレクションの設計に関連する可能性のある重要な点です。
-
TODO項目の削除:
Todo's
セクションから[ ] cleanup convert() vs T() vs x.(T) - convert() should go away?
がClosed
セクションに移動し、完了した項目としてマークされました。これは、convert()
という概念がGo言語の最終的な仕様から削除される方向で検討が進んでいたことを示唆しています。Predeclared functions
リストからconvert
とtypeof
が削除されました。これは、これらの関数がGo言語の組み込み関数としては採用されないことが決定されたことを意味します。typeof
はJavaScriptのような動的型付け言語で使われる概念であり、Goのような静的型付け言語には不要と判断されたと考えられます。
これらの変更は、Go言語の仕様が初期段階からどのように洗練され、明確化されていったかを示す良い例です。特にポインタとメソッド値に関する記述の追加・整理は、Go言語の基本的なセマンティクスを確立する上で重要なステップでした。
コアとなるコードの変更箇所
変更はすべて doc/go_spec.html
ファイルに対して行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -22,7 +22,6 @@ Todo's:
[ ] need to talk about precise int/floats clearly
[ ] iant suggests to use abstract/precise int for len(), cap() - good idea
(issue: what happens in len() + const - what is the type?)
-[ ] cleanup convert() vs T() vs x.(T) - convert() should go away?
[ ] fix "else" part of if statement
[ ] cleanup: 6g allows: interface { f F } where F is a function type.
fine, but then we should also allow: func f F {}, where F is a function type.
@@ -124,6 +123,7 @@ Closed:
and if so, does a label followed by an empty statement (a semicolon) still denote
a for loop that is following, and can break L be used inside it?
[x] there is some funniness regarding ';' and empty statements and label decls
+[x] cleanup convert() vs T() vs x.(T) - convert() should go away?
-->
@@ -1403,7 +1403,6 @@ Constants:
Functions:
cap len make new panic panicln print println
- (TODO: typeof??)
Packages:
sys (TODO: does sys endure?)
@@ -2664,17 +2663,30 @@ The right operand is evaluated conditionally.
<h3>Address operators</h3>
-<!--TODO(r): This section is a mess. Skipping it for now.-->
-
<p>
-<font color=red>TODO: Need to talk about unary "*", clean up section below.</font>
+The unary prefix address-of operator <code>&</code> generates the address of its operand, which must be a variable,
+pointer indirection, field selector, or array or slice indexing operation. It is illegal to take the address of a function
+result variable.
+Given an operand of pointer type, the unary prefix pointer indirection operator <code>*</code> retrieves the value pointed
+to by the operand.
+</p>
+
+<pre>
+&x
+&a[f(2)]
+*p
+*pf(x)
+</pre>
+
<p>
<font color=red>TODO: This text needs to be cleaned up and go elsewhere, there are no address
operators involved.
</font>
+</p>
<p>
-Methods are a form of function, and a method ``value'' has a function type.
+Methods are a form of function and a method ``value'' has a function type.
Consider the type T with method M:
+</p>
<pre>
type T struct {
@@ -2684,25 +2696,33 @@ func (tp *T) M(a int) int;
var t *T;
</pre>
+<p>
To construct the value of method M, one writes
+</p>
<pre>
t.M
</pre>
+<p>
using the variable t (not the type T).
<font color=red>TODO: It makes perfect sense to be able to say T.M (in fact, it makes more
sense then t.M, since only the type T is needed to find the method M, i.e.,
its address). TBD.
</font>
+</p>
+<p>
The expression t.M is a function value with type
+</p>
<pre>
func (t *T, a int) int
</pre>
+<p>
and may be invoked only as a function, not as a method:
+</p>
<pre>
var f func (t *T, a int) int;
@@ -2710,30 +2730,39 @@ f = t.M;
x := f(t, 7);
</pre>
+<p>
Note that one does not write t.f(7); taking the value of a method demotes
it to a function.
+</p>
+<p>
In general, given type T with method M and variable t of type T,
the method invocation
+</p>
<pre>
t.M(args)
</pre>
+<p>
is equivalent to the function call
+</p>
<pre>
(t.M)(t, args)
</pre>
+<p>
<font color=red>
TODO: should probably describe the effect of (t.m) under §Expressions if t.m
denotes a method: Effect is as described above, converts into function.
</font>
+</p>
<p>
If T is an interface type, the expression t.M does not determine which
underlying type's M is called until the point of the call itself. Thus given
T1 and T2, both implementing interface I with method M, the sequence
+</p>
<pre>
var t1 *T1;
@@ -2743,8 +2772,10 @@ m := i.M;
m(t2, 7);
</pre>
+<p>
will invoke t2.M() even though m was constructed with an expression involving
t1. Effectively, the value of m is a function literal
+</p>
<pre>
func (recv I, a int) {
@@ -2752,13 +2783,16 @@ func (recv I, a int) {
}
</pre>
+<p>
that is automatically created.
+</p>
<p>
<font color=red>
TODO: Document implementation restriction: It is illegal to take the address
of a result parameter (e.g.: func f() (x int, p *int) { return 2, &x }).
(TBD: is it an implementation restriction or fact?)
</font>
+</p>
<h3>Communication operators</h3>
@@ -3131,11 +3165,13 @@ if x := f(); x < y {\
An expression or type specifier is compared to the "cases"
inside the "switch" to determine which branch
to execute.
+</p>
<pre class="grammar">\
SwitchStat = ExprSwitchStat | TypeSwitchStat .
</pre>
+<p>
There are two forms: expression switches and type switches.
In an expression switch, the cases contain expressions that are compared
against the value of the switch expression.
@@ -3690,7 +3726,6 @@ for i := 0; i <= 3; i++ {\
<h2>Predeclared functions</h2>
<ul>
<li>cap
- <li>convert
<li>len
<li>make
<li>new
@@ -3698,7 +3733,6 @@ for i := 0; i <= 3; i++ {\
<li>panicln
<li>print
<li>println
- <li>typeof
</ul>
<h3>Length and capacity</h3>
コアとなるコードの解説
このコミットの主要な変更点は、Go言語の仕様書 doc/go_spec.html
におけるポインタ演算子 (&
と *
) の説明の追加と、HTMLの整形、そしていくつかのTODO項目の整理です。
-
アドレス演算子 (
&
) とポインタ間接演算子 (*
) の説明の追加:- 以前はコメントアウトされていた「Address operators」セクションが、具体的な説明で置き換えられました。
&
演算子については、その機能(オペランドのアドレスを生成する)と、有効なオペランドの種類(変数、ポインタ間接、フィールドセレクタ、配列/スライスインデックス操作)が明記されました。特に、「関数結果変数のアドレスを取ることは不正である」という重要な制約が追加されています。これは、Go言語のメモリ管理モデル(特にスタックとヒープの扱い)と密接に関連しており、一時的な値のアドレスを返すことによるダングリングポインタの発生を防ぐための設計上の決定です。*
演算子については、その機能(ポインタが指し示す値を取得する)が簡潔に説明されました。- これらの説明に続いて、実際のコード例 (
&x
,&a[f(2)]
,*p
,*pf(x)
) が<pre>
タグで示され、読者が具体的な使用方法を理解しやすくなっています。
-
HTMLの整形と構造の改善:
- 多くの箇所で、既存のテキストが
<p>
(段落) タグで囲まれるようになりました。これは、HTMLのセマンティクスを改善し、ドキュメントの論理的な構造をより明確にするための変更です。これにより、ブラウザでの表示がより適切になり、将来的なスタイル適用や解析が容易になります。 - 特にメソッド値に関するセクションでは、各説明ブロックが独立した段落として扱われるようになり、情報の区切りが明確になりました。
- 多くの箇所で、既存のテキストが
-
TODO項目の整理と削除:
Todo's
セクションから[ ] cleanup convert() vs T() vs x.(T) - convert() should go away?
が削除され、Closed
セクションに[x] cleanup convert() vs T() vs x.(T) - convert() should go away?
として追加されました。これは、convert()
という概念がGo言語の仕様から削除される方向で決定されたことを示しています。初期のGo言語には、型変換のためのconvert()
という組み込み関数が検討されていた時期がありましたが、最終的にはT(x)
のような型キャスト構文や、x.(T)
のような型アサーションが採用されました。Predeclared functions
リストからconvert
とtypeof
が削除されました。これは、これらの関数がGo言語の組み込み関数としては採用されないことが確定したことを意味します。typeof
は、JavaScriptのような動的型付け言語で実行時に変数の型を調べるために使われる演算子ですが、Go言語は静的型付け言語であるため、コンパイル時に型が決定されるため、このような組み込み関数は不要と判断されたと考えられます。
-
メソッド値に関する新しいTODOの追加:
- メソッド値のセクションに、
TODO: should probably describe the effect of (t.m) under §Expressions if t.m denotes a method: Effect is as described above, converts into function.
というTODOが追加されました。これは、メソッド値がどのように関数に「デモート」されるかという挙動を、「式」に関するより一般的なセクションで詳細に説明する必要があるという認識を示しています。 - さらに、
TODO: Document implementation restriction: It is illegal to take the address of a result parameter (e.g.: func f() (x int, p *int) { return 2, &x }). (TBD: is it an implementation restriction or fact?)
というTODOも追加されました。これは、関数から返される値のアドレスを取得することの禁止について、それが実装上の制限なのか、それとも言語仕様上の事実なのかを明確にする必要があるという、より深い言語設計上の問いかけです。これは、Go言語のコンパイラがどのように最適化を行うか、あるいはガベージコレクタがどのようにメモリを管理するかといった、ランタイムの挙動にも関わる重要な論点です。
- メソッド値のセクションに、
これらの変更は、Go言語の仕様が初期段階からどのように進化し、より厳密で明確なものになっていったかを示す貴重な記録です。特に、ポインタとメソッド値というGo言語の根幹をなす概念に関する記述の洗練は、言語の安定性と一貫性を確保する上で不可欠なステップでした。
関連リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書 (現在のバージョン): https://go.dev/ref/spec
- Go言語の歴史: https://go.dev/doc/history
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語の初期のメーリングリストやデザインドキュメント (Goの歴史に関する情報源から辿れる場合があります)
- HTML Tidy: http://www.html-tidy.org/ (HTML整形ツールの一般的な情報)
- Go言語のポインタに関する解説記事 (現在のGo言語のポインタの挙動を理解するため):
- https://go.dev/tour/moretypes/1 (Go Tour: Pointers)
- https://go.dev/blog/go-pointers (Go Blog: Go Pointers)
- Go言語のメソッド値に関する解説記事 (現在のGo言語のメソッド値の挙動を理解するため):
- https://go.dev/tour/methods/4 (Go Tour: Methods continued)
- https://go.dev/blog/laws-of-reflection (Go Blog: The Laws of Reflection - メソッド値にも関連する概念)