[インデックス 11823] ファイルの概要
このコミットは、Go言語の仕様書(doc/go_spec.html
)に対する変更であり、メソッド名の一意性に関する記述を明確にすることを目的としています。具体的には、メソッドセット内でのメソッド名の一意性要件を強調し、関数宣言とメソッド宣言の構文定義に「関数名 (FunctionName)」と「メソッド名 (MethodName)」という概念を導入しています。
コミット
commit b1d9ae9406e0217731665da622b7a29fadc3efbd
Author: Robert Griesemer <gri@golang.org>
Date: Sun Feb 12 20:03:30 2012 -0800
go spec: method names must be unique
Fixes #2916.
R=golang-dev, remyoudompheng, r, rsc
CC=golang-dev
https://golang.org/cl/5652064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b1d9ae9406e0217731665da622b7a29fadc3efbd
元コミット内容
このコミットの元の内容は、Go言語の仕様書において、メソッドセット内のメソッド名が一意でなければならないという規則をより明確にすることです。また、関数とメソッドの宣言における「名前」の概念をより厳密に定義しています。これは、Go言語のコンパイラやツールが、メソッドの解決や識別を正しく行うための基盤となる重要な仕様変更です。
変更の背景
この変更は、Go言語のIssue #2916「spec: method names must be unique
」を修正するために行われました。このIssueは、Go言語の仕様書において、メソッドセット内でのメソッド名の一意性に関する記述が曖昧であったり、不十分であったりしたために提起されたと考えられます。
Go言語では、型に紐付けられた関数を「メソッド」と呼びます。メソッドはレシーバ(receiver)と呼ばれる特別な引数を持ち、これにより特定の型に関連付けられます。複数のメソッドが同じ型に属する場合、それらは「メソッドセット」を形成します。もしメソッドセット内に同じ名前のメソッドが複数存在することを許容してしまうと、コンパイラがどのメソッドを呼び出すべきかを判断できなくなり、曖昧性や予期せぬ動作を引き起こす可能性があります。
このコミット以前の仕様書では、この一意性に関する記述が不十分であったため、実装者やGo言語の学習者が混乱する可能性がありました。そのため、Robert Griesemer氏(Go言語の共同設計者の一人)によって、この重要な規則を明確にするための変更が提案・実装されました。
前提知識の解説
Go言語のメソッドとレシーバ
Go言語において、メソッドは特定の型に関連付けられた関数です。メソッドは、その定義において「レシーバ」と呼ばれる特別な引数を持ちます。レシーバは、メソッドがどの型の値に対して操作を行うかを示します。
例:
type MyType struct {
value int
}
// MyType のメソッド
func (m MyType) GetValue() int {
return m.value
}
// *MyType (ポインタ) のメソッド
func (m *MyType) SetValue(newValue int) {
m.value = newValue
}
上記の例では、GetValue
はMyType
型の値レシーバを持つメソッドであり、SetValue
は*MyType
型のポインタレシーバを持つメソッドです。
メソッドセット (Method Set)
Go言語の型には「メソッドセット」と呼ばれる概念があります。これは、その型が持つことができるメソッドの集合を定義します。
- 型
T
のメソッドセット: レシーバがT
であるすべてのメソッドが含まれます。 - 型
*T
のメソッドセット: レシーバが*T
であるすべてのメソッドと、レシーバがT
であるすべてのメソッドが含まれます。つまり、ポインタ型は値型のメソッドセットも継承します。
このメソッドセットの概念は、インターフェースの実装や型の振る舞いを理解する上で非常に重要です。
識別子 (Identifier)
プログラミング言語において、識別子とは変数、関数、型、メソッドなどの名前として使用される文字列のことです。Go言語では、識別子は文字またはアンダースコアで始まり、その後に文字、数字、またはアンダースコアが続きます。
EBNF (Extended Backus-Naur Form)
EBNFは、プログラミング言語の構文を記述するためのメタ言語です。このコミットのdiffに含まれる<pre class="ebnf">
タグ内の記述は、Go言語の構文規則をEBNF形式で示しています。
=
: 定義|
: または[]
: オプション(0回または1回){}
: 0回以上の繰り返し""
: リテラル(そのままの文字列)
技術的詳細
このコミットは、Go言語の仕様書(doc/go_spec.html
)の以下のセクションに影響を与えています。
-
メソッドセットの定義の明確化:
- 変更前:
In a method set, each method must have a unique name.
- 変更後:
In a method set, each method must have a unique <a href="#MethodName">method name</a>.
- これは、単に「名前」ではなく、明確に「メソッド名」が一意でなければならないことを示し、
MethodName
という新しいアンカーリンク(#MethodName
)を導入しています。これにより、読者は「メソッド名」の定義に直接ジャンプできるようになります。
- これは、単に「名前」ではなく、明確に「メソッド名」が一意でなければならないことを示し、
- 変更前:
-
関数宣言の構文定義の変更:
- 変更前:
FunctionDecl = "func" identifier Signature [ Body ] .
- 変更後:
FunctionDecl = "func" FunctionName Signature [ Body ] . FunctionName = identifier .
identifier
を直接使用する代わりに、FunctionName
という新しい非終端記号を導入し、FunctionName
がidentifier
であることを定義しています。これにより、関数宣言における「関数名」という概念がより明示的になります。
- 変更前:
-
メソッド宣言の構文定義の変更とメソッド名の一意性規則の追加:
-
変更前:
A method declaration binds an identifier to a method.
-
変更後:
A method declaration binds an identifier, the <i>method name</i>, to a method. It also associates the method with the receiver's <i>base type</i>.
- メソッド宣言が「識別子」を「メソッド名」としてバインドし、そのメソッドがレシーバの「基底型 (base type)」に関連付けられることを明確にしています。
-
さらに、以下の新しい段落が追加されました。
<p> For a base type, the non-<a href="#Blank_identifier">blank</a> names of methods bound to it must be unique. If the base type is a <a href="#Struct_types">struct type</a>, the non-blank method and field names must be distinct. </p>
- この追加は、このコミットの核心部分です。
- 「基底型 (base type)」に対して、ブランク識別子(
_
)ではないメソッド名が一意でなければならないことを明記しています。 - もし基底型が構造体型(
struct type
)である場合、ブランクではないメソッド名とフィールド名が互いに異なる(distinct)でなければならないという、より具体的な規則が追加されています。これは、構造体のフィールド名とメソッド名が衝突することを防ぐための重要な規則です。
-
これらの変更は、Go言語のコンパイラがメソッドの解決を行う際の曖昧さを排除し、言語の整合性を保つ上で不可欠な仕様の明確化です。特に、構造体のフィールド名とメソッド名の衝突に関する規則は、Go言語の設計思想である「シンプルさ」と「明確さ」を反映しています。
コアとなるコードの変更箇所
変更はすべて doc/go_spec.html
ファイル内で行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -662,7 +662,7 @@ The method set of the corresponding pointer type <code>*T</code>
is the set of all methods with receiver <code>*T</code> or <code>T</code>
(that is, it also contains the method set of <code>T</code>).
Any other type has an empty method set.
-In a method set, each method must have a unique name.
+In a method set, each method must have a unique <a href="#MethodName">method name</a>.
</p>
<p>
@@ -1862,11 +1862,13 @@ they can be used to declare local temporary variables (§<a href="#Statements">S
<h3 id="Function_declarations">Function declarations</h3>
<p>
-A function declaration binds an identifier to a function (§<a href="#Function_types">Function types</a>).
+A function declaration binds an identifier, the <i>function name</i>,
+to a function.
</p>
<pre class="ebnf">
-FunctionDecl = "func" identifier Signature [ Body ] .
+FunctionDecl = "func" FunctionName Signature [ Body ] .
+FunctionName = identifier .
Body = Block .
</pre>
@@ -1890,8 +1892,10 @@ func flushICache(begin, end uintptr) // implemented externally
<p>
A method is a function with a <i>receiver</i>.
-A method declaration binds an identifier to a method.
+A method declaration binds an identifier, the <i>method name</i>, to a method.
+It also associates the method with the receiver's <i>base type</i>.
</p>
+\
<pre class="ebnf">
MethodDecl = "func" Receiver MethodName Signature [ Body ] .
Receiver = "(\" [ identifier ] [ \"*\" ] BaseTypeName \")\" .
@@ -1900,13 +1904,18 @@ BaseTypeName = identifier .\
<p>
The receiver type must be of the form <code>T</code> or <code>*T</code> where
-<code>T</code> is a type name. <code>T</code> is called the
-<i>receiver base type</i> or just <i>base type</i>.\
-The base type must not be a pointer or interface type and must be
-declared in the same package as the method.\
-The method is said to be <i>bound</i> to the base type\
-and is visible only within selectors for that type\
-(§<a href="#Type_declarations">Type declarations</a>, §<a href="#Selectors">Selectors</a>).\
+<code>T</code> is a type name. The type denoted by <code>T</code> is called
+the receiver <i>base type</i>; it must not be a pointer or interface type and
+it must be declared in the same package as the method.
+The method is said to be <i>bound</i> to the base type and the method name
+is visible only within selectors for that type.
+</p>
+\
+<p>
+For a base type, the non-<a href="#Blank_identifier">blank</a> names of
+methods bound to it must be unique.
+If the base type is a <a href="#Struct_types">struct type</a>,
+the non-blank method and field names must be distinct.
</p>
\
<p>
コアとなるコードの解説
このコミットの主要な変更点は、Go言語の仕様書におけるメソッド名の一意性に関する記述の強化と、関数およびメソッドの命名規則の明確化です。
-
メソッドセットにおけるメソッド名の一意性:
- 以前は「各メソッドは一意の名前を持たなければならない」とだけ書かれていましたが、これを「各メソッドは一意のメソッド名を持たなければならない」と修正し、
#MethodName
へのリンクを追加しました。これにより、「メソッド名」という用語が仕様書内で一貫して定義され、参照可能になりました。
- 以前は「各メソッドは一意の名前を持たなければならない」とだけ書かれていましたが、これを「各メソッドは一意のメソッド名を持たなければならない」と修正し、
-
関数宣言の構文定義の明確化:
FunctionDecl = "func" identifier Signature [ Body ] .
から
へと変更されました。FunctionDecl = "func" FunctionName Signature [ Body ] . FunctionName = identifier .
- これは、関数宣言における「関数名」という概念を明示的に導入し、それが識別子であることを定義しています。これにより、仕様書を読む際に、関数名が単なる識別子ではなく、特定の役割を持つ名前であることがより明確になります。
-
メソッド宣言の記述の拡張と新しい規則の追加:
- メソッド宣言が「識別子」を「メソッド名」としてバインドし、レシーバの「基底型」に関連付けられることを明確にしました。
- 最も重要な追加は、以下の新しい段落です。
<p> For a base type, the non-<a href="#Blank_identifier">blank</a> names of methods bound to it must be unique. If the base type is a <a href="#Struct_types">struct type</a>, the non-blank method and field names must be distinct. </p>
- この記述は、Go言語の型システムにおける重要な制約を定義しています。
- 「基底型に対して、ブランク識別子ではないメソッド名が一意でなければならない」: これは、同じ基底型に対して同じ名前のメソッドを複数定義できないことを意味します。例えば、
type MyType int
に対してfunc (m MyType) Foo() {}
とfunc (m MyType) Foo() {}
のように同じ名前のメソッドを定義することはできません。これはコンパイラがどのFoo
メソッドを呼び出すべきか判断できないためです。 - 「もし基底型が構造体型である場合、ブランクではないメソッド名とフィールド名が互いに異なるでなければならない」: これは、構造体のフィールド名と、その構造体に紐付けられたメソッド名が衝突してはならないという規則です。例えば、
type MyStruct struct { Foo int }
という構造体がある場合、func (s MyStruct) Foo() {}
というメソッドを定義することはできません。これは、s.Foo
というセレクタがフィールドを参照するのか、メソッドを参照するのか曖昧になるためです。この規則により、Go言語のセレクタの解決が常に明確になります。
これらの変更は、Go言語のコンパイラがコードを解析し、メソッド呼び出しを解決する際の基盤となる規則をより厳密に定義するものです。これにより、Go言語のコードの曖昧さが減り、予測可能な動作が保証されます。
関連リンク
- Go Issue #2916: https://github.com/golang/go/issues/2916
- Go Code Review (CL) 5652064: https://golang.org/cl/5652064 (これは古いGoのコードレビューシステムへのリンクであり、現在はGitHubのコミットページにリダイレクトされるか、アクセスできない場合があります。)
参考にした情報源リンク
- Go言語の公式ドキュメント: https://go.dev/doc/
- Go言語の仕様書: https://go.dev/ref/spec (このコミットが変更した内容の最新版が掲載されています)
- Go言語のメソッドセットに関する解説記事 (例: A Tour of Go, Effective Goなど)
- EBNF (Extended Backus-Naur Form) に関する情報
- Go言語のIssueトラッカー (GitHub Issues)
- Go言語のコードレビューシステム (Gerrit)