[インデックス 14489] ファイルの概要
このコミットは、Go言語の公式仕様書である doc/go_spec.html
ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、および標準ライブラリの動作を詳細に記述した、Goプログラマーにとって最も権威あるリファレンスの一つです。具体的には、メソッド呼び出しに関する例が修正されています。
コミット
- コミットハッシュ:
d4f3185c24e3816acfa5760a0dc4b40b20867867
- Author: Russ Cox rsc@golang.org
- Date: Mon Nov 26 15:43:32 2012 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d4f3185c24e3816acfa5760a0dc4b40b20867867
元コミット内容
spec: add () to method calls in examples
Since p.M is rejected (must call methods), use p.M(),
to keep the examples compiling.
Fixes #4441.
R=gri
CC=golang-dev
https://golang.org/cl/6854096
変更の背景
このコミットの背景には、Go言語の仕様書に記載されているコード例が、実際のGoコンパイラではコンパイルエラーとなるという問題がありました。具体的には、メソッドを呼び出す際に括弧 ()
が不足しているという問題です。
Go言語では、構造体やインターフェースに紐付けられた関数を「メソッド」と呼びます。これらのメソッドを実際に実行(呼び出し)するには、関数と同様にメソッド名の後に ()
を付ける必要があります。しかし、仕様書の一部の例では、メソッドを呼び出す意図であるにもかかわらず、単に p.M
のようにメソッド名だけが記述されていました。
p.M
という記述は、Goにおいては「メソッド値 (method value)」と呼ばれるものを取得する操作であり、メソッドを即座に実行する「メソッド呼び出し (method call)」ではありません。メソッド値は、そのメソッドを後で呼び出すための関数として扱うことができます。この違いにより、仕様書の例が読者に誤解を与える可能性があり、また、その例を実際に試した際にコンパイルエラーが発生するという不整合が生じていました。
この問題は、GoのIssueトラッカーで #4441
として報告されており、このコミットはその問題を修正するために行われました。仕様書は言語の正確な定義であるため、その中のコード例が実際に動作することは非常に重要です。
前提知識の解説
このコミットを理解するためには、Go言語における「メソッド」と「メソッド呼び出し」、「メソッド値」の概念を理解しておく必要があります。
メソッド (Methods)
Go言語では、関数を型に関連付けることができます。これをメソッドと呼びます。メソッドは、レシーバ引数(receiver argument)と呼ばれる特別な引数を持つ関数として定義されます。レシーバは、メソッドが操作する値またはポインタを指定します。
例:
type MyStruct struct {
value int
}
func (s MyStruct) GetValue() int { // MyStruct 型のメソッド
return s.value
}
func (s *MyStruct) SetValue(newValue int) { // *MyStruct 型のメソッド
s.value = newValue
}
メソッド呼び出し (Method Calls)
メソッドを実際に実行するには、レシーバの後にドット .
とメソッド名を記述し、その後に引数リストを括弧 ()
で囲んで指定します。引数がない場合でも ()
は必須です。
例:
s := MyStruct{value: 10}
val := s.GetValue() // メソッド呼び出し
s.SetValue(20) // メソッド呼び出し
メソッド値 (Method Values)
Goでは、メソッドを関数値として扱うことができます。これは「メソッド値」と呼ばれます。メソッド値は、特定のレシーバにバインドされたメソッドを表す関数です。メソッド値を取得するには、メソッド名の後に ()
を付けずに記述します。
例:
s := MyStruct{value: 10}
f := s.GetValue // GetValue メソッドを s にバインドした関数値を取得
val := f() // 後で関数 f を呼び出す
この f
は、func() int
型の関数として扱うことができます。s.GetValue
は s
の GetValue
メソッドを指す関数値であり、s.GetValue()
はその関数値を呼び出す操作です。
メソッド式 (Method Expressions)
メソッド値と似ていますが、メソッド式はレシーバにバインドされていないメソッドを表す関数です。メソッド式は、レシーバの型を明示的に指定して取得します。
例:
f := MyStruct.GetValue // MyStruct 型の GetValue メソッドを表す関数値を取得
s := MyStruct{value: 10}
val := f(s) // 関数 f を呼び出す際にレシーバ s を引数として渡す
この f
は、func(MyStruct) int
型の関数として扱うことができます。
このコミットの修正は、仕様書内の例が「メソッド呼び出し」を意図しているにもかかわらず、「メソッド値の取得」の構文になっていた点を修正するものです。
技術的詳細
このコミットは、Go言語の仕様書 doc/go_spec.html
内の「セレクタ (Selectors)」のセクションにあるコード例を修正しています。セレクタは、構造体のフィールドやメソッドにアクセスするための構文を定義するものです。
問題となっていたのは、以下の形式の例でした。
p.M2 // (*p).M2
p.M1 // ((*p).T1).M1
p.M0 // ((*p).T0).M0
これらの行は、p
という変数からメソッド M2
, M1
, M0
にアクセスする例を示しています。しかし、Go言語の構文規則では、メソッドを「呼び出す」ためには、メソッド名の後に必ず括弧 ()
を付ける必要があります。括弧がない場合、それはメソッド自体を関数値として取得する操作と解釈されます。
仕様書は言語の動作を正確に記述するものであるため、例もまた正確である必要があります。上記の例は、メソッドを呼び出す意図で書かれているにもかかわらず、構文的にはメソッド値を取得する形になっていました。これにより、読者がこれらの例をそのままGoのコードとして実行しようとすると、コンパイルエラーが発生するか、意図しない動作(メソッドが実行されない)となる可能性がありました。
このコミットでは、これらの例に ()
を追加することで、実際にメソッドが呼び出される正しい構文に修正しています。これにより、仕様書の例がGo言語の実際の動作と一致し、読者の混乱を防ぐことができます。
コアとなるコードの変更箇所
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
-" "Subtitle": "Version of November 21, 2012",
+" "Subtitle": "Version of November 26, 2012",
"Path": "/ref/spec"
}-->
@@ -2463,9 +2463,9 @@ p.z // (*p).z
p.y // ((*p).T1).y
p.x // (*(*p).T0).x
-p.M2 // (*p).M2
-p.M1 // ((*p).T1).M1
-p.M0 // ((*p).T0).M0
+p.M2() // (*p).M2()
+p.M1() // ((*p).T1).M1()
+p.M0() // ((*p).T0).M0()
</pre>
コアとなるコードの解説
このdiffは、doc/go_spec.html
ファイルの2つの部分に変更を加えていることを示しています。
-
ファイルのバージョン情報の更新:
- "Subtitle": "Version of November 21, 2012", + "Subtitle": "Version of November 26, 2012",
これは、仕様書のバージョン日付を
November 21, 2012
からNovember 26, 2012
に更新しています。これは、ドキュメントが更新されたことを示す標準的な慣行です。 -
メソッド呼び出し例の修正:
-p.M2 // (*p).M2 -p.M1 // ((*p).T1).M1 -p.M0 // ((*p).T0).M0 +p.M2() // (*p).M2() +p.M1() // ((*p).T1).M1() +p.M0() // ((*p).T0).M0()
この部分がこのコミットの主要な変更点です。
- 元のコードでは、
p.M2
,p.M1
,p.M0
と記述されていました。これはGo言語では「メソッド値の取得」と解釈されます。つまり、p
に関連付けられたM2
メソッドを指す関数値を取得する操作です。 - 修正後のコードでは、これらの行が
p.M2()
,p.M1()
,p.M0()
と変更されています。メソッド名の後に()
を追加することで、これらは「メソッド呼び出し」となり、実際にメソッドが実行されることを意味します。 - コメント部分
// (*p).M2
なども、それに合わせて// (*p).M2()
のように修正されています。これは、レシーバがポインタ型の場合に、自動的にデリファレンスされてメソッドが呼び出されることを示唆しています。
- 元のコードでは、
この変更により、Go言語の仕様書に記載されているコード例が、Goコンパイラで正しくコンパイルされ、意図した通りにメソッドが呼び出されるようになります。これは、仕様書の正確性と、読者への誤解を避ける上で非常に重要な修正です。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/d4f3185c24e3816acfa5760a0dc4b40b20867867
- Go Issue #4441: https://code.google.com/p/go/issues/detail?id=4441 (古いGoogle Codeのリンクですが、GoのIssueトラッカーでこのコミットが修正した問題です)
- Go CL 6854096: https://golang.org/cl/6854096 (GoのコードレビューシステムであるGerritのチェンジリストへのリンク)
参考にした情報源リンク
- The Go Programming Language Specification: https://go.dev/ref/spec (特に「Selectors」および「Calls」のセクション)
- Effective Go - Methods: https://go.dev/doc/effective_go#methods (Goにおけるメソッドの概念と使用法に関する公式ドキュメント)
- Go by Example: Methods: https://gobyexample.com/methods (Goのメソッドに関する実践的な例)
- Go: Method Values and Method Expressions: https://www.ardanlabs.com/blog/2013/09/go-method-values-and-method-expressions.html (Goにおけるメソッド値とメソッド式の違いに関する解説記事)