Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13282] ファイルの概要

このコミットは、Go言語の公式仕様書 doc/go_spec.html における、匿名フィールドのメソッドおよびフィールドの「プロモーション(昇格)」に関する規則の記述を明確化するものです。特に、構造体とそのポインタ型のメソッドセットに、匿名フィールドのメソッドがどのように含まれるかについて、より詳細かつ正確な説明が追加されています。

コミット

commit 787adb6eb3cbb7a8d07700739f008f0b250be55f
Author: Robert Griesemer <gri@golang.org>
Date:   Mon Jun 4 14:24:10 2012 -0700

    go spec: clarify promotion rules for methods/fields of anonymous fields
    
    Fixes #3635.
    
    R=rsc, r, iant, kevlar, iant
    CC=golang-dev
    https://golang.org/cl/6217045

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/787adb6eb3cbb7a8d07700739f008f0b250be55f

元コミット内容

このコミットは、Go言語の仕様書の一部を修正し、匿名フィールドのメソッドとフィールドのプロモーション規則をより明確にすることを目的としています。具体的には、以下の点が変更されています。

  • 匿名フィールドのフィールドとメソッドが「プロモートされる」ことの定義を明確化。
  • プロモートされたフィールドが構造体リテラルで使用できないことに関する注意書きの追加。
  • 構造体 S とそのポインタ型 *S のメソッドセットに、匿名フィールド T または *T のメソッドがどのように含まれるかについての規則を詳細化。

この変更は、Issue #3635 に対応するものです。

変更の背景

Go言語の初期の仕様では、匿名フィールドのプロモーション規則、特にメソッドセットへの影響について、一部曖昧な点がありました。これにより、開発者が匿名フィールドを使用する際に、メソッドの可視性や呼び出しに関する誤解が生じる可能性がありました。

このコミットは、その曖昧さを解消し、仕様をより厳密かつ理解しやすくすることを目的としています。特に、構造体型 S とそのポインタ型 *S の両方で、匿名フィールドのメソッドがどのように「昇格」され、メソッドセットに含まれるのかを明確にすることで、Go言語の型システムの一貫性と予測可能性を高めています。

Issue #3635 は、この仕様の曖昧さによって引き起こされた具体的な問題や混乱を指摘していたと考えられます。この修正は、Go言語の設計思想である「シンプルさ」と「明確さ」を追求する一環として行われました。

前提知識の解説

このコミットの理解には、以下のGo言語の概念が不可欠です。

1. 構造体 (Structs)

Go言語の構造体は、異なる型のフィールドをまとめるための複合データ型です。C言語の構造体やC++のクラスに似ていますが、Goの構造体はメソッドを持つことができます。

type Person struct {
    Name string
    Age  int
}

2. 匿名フィールド (Anonymous Fields / Embedded Fields)

Go言語の構造体には、フィールド名を指定せずに型のみを指定する「匿名フィールド」という特殊な機能があります。これにより、ある構造体が別の構造体のフィールドやメソッドを「継承」しているかのように振る舞うことができます。これは、Goにおけるコンポジション(合成)によるコードの再利用の主要なメカニズムです。

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "..."
}

type Dog struct {
    Animal // 匿名フィールド
    Breed  string
}

func main() {
    d := Dog{Animal: Animal{Name: "Buddy"}, Breed: "Golden Retriever"}
    fmt.Println(d.Name)  // AnimalのNameフィールドにアクセス
    fmt.Println(d.Speak()) // AnimalのSpeakメソッドにアクセス
}

上記の例では、Dog 構造体は Animal 構造体を匿名フィールドとして含んでいます。これにより、Dog のインスタンスから直接 Animal のフィールド (Name) やメソッド (Speak) にアクセスできます。このアクセスを「プロモーション(昇格)」と呼びます。

3. メソッドセット (Method Sets)

Go言語では、各型には「メソッドセット」と呼ばれる、その型に関連付けられたメソッドの集合があります。メソッドセットは、インターフェースの適合性を判断する上で非常に重要です。

  • T のメソッドセット: レシーバが T であるすべてのメソッド。
  • *T (ポインタ型) のメソッドセット: レシーバが T または *T であるすべてのメソッド。

重要なのは、ポインタ型 *T のメソッドセットは、非ポインタ型 T のメソッドセットを常に含むという点です。これは、Goがポインタレシーバのメソッドを値レシーバの変数から呼び出すことを許可しているためです(その逆は不可)。

4. セレクタ (Selectors)

セレクタは、構造体のフィールドやメソッドにアクセスするための構文です。例えば、x.f は構造体 x のフィールド f またはメソッド f を選択します。匿名フィールドの場合、プロモーションによって、埋め込まれた型のフィールドやメソッドが、外側の構造体の直接のフィールドやメソッドであるかのようにセレクタでアクセスできるようになります。

技術的詳細

このコミットの技術的詳細は、Go言語の型システムにおける匿名フィールドのプロモーション規則の厳密な定義にあります。

1. 「プロモートされた」フィールド/メソッドの定義の明確化

変更前は、「匿名フィールドのフィールドとメソッドは、構造体の通常のフィールドとメソッドとしてプロモートされる」と漠然と記述されていました。 変更後は、より厳密に「構造体 x 内の匿名フィールドのフィールドまたはメソッド f は、x.f がそのフィールドまたはメソッド f を示す有効なセレクタである場合に プロモートされた と呼ばれる」と定義されています。これは、プロモーションがセレクタによるアクセス可能性に直接関連していることを明確にしています。

2. プロモートされたフィールドの制約の追加

「プロモートされたフィールドは、構造体の複合リテラルにおいてフィールド名として使用できない」という重要な制約が追加されました。これは、匿名フィールドのフィールドが外側の構造体の「通常のフィールド」のように振る舞う一方で、初期化の際にはその匿名性(埋め込み元)を考慮する必要があるという、Go言語の設計上の特性を反映しています。

例:

type Inner struct {
    Value int
}

type Outer struct {
    Inner
}

func main() {
    // OK: Innerを匿名フィールドとして初期化
    o1 := Outer{Inner: Inner{Value: 10}} 
    
    // NG (変更後の仕様): プロモートされたフィールドを直接使用して初期化することはできない
    // o2 := Outer{Value: 20} // コンパイルエラー
}

3. メソッドセットへのプロモーション規則の精緻化

最も重要な変更点は、構造体 S とそのポインタ型 *S のメソッドセットに、匿名フィールドのメソッドがどのように含まれるかに関する規則の精緻化です。

変更前の規則(簡略化):

  • S が匿名フィールド T を含む場合、S のメソッドセットは T のメソッドセットを含む。
  • S が匿名フィールド *T を含む場合、S のメソッドセットは *T のメソッドセットを含む。
  • S が匿名フィールド T または *T を含む場合、*S のメソッドセットは *T のメソッドセットを含む。

これらの規則は、特にレシーバの型(値レシーバ T vs ポインタレシーバ *T)と、外側の構造体(S vs *S)の組み合わせにおいて、曖昧さや不完全さがありました。

変更後の規則(詳細化):

  • S が匿名フィールド T を含む場合:

    • S および *S の両方のメソッドセットは、レシーバが T であるプロモートされたメソッドを含む。
    • *S のメソッドセットは、さらにレシーバが *T であるプロモートされたメソッドも含む。

    これは、S の値レシーバメソッドは T の値レシーバメソッドのみをプロモートするが、S のポインタレシーバメソッドは T の値レシーバメソッドとポインタレシーバメソッドの両方をプロモートするという、Goのメソッドセットの基本ルール(ポインタ型は値型とポインタ型の両方のメソッドを持つ)を匿名フィールドのプロモーションにも適用していることを明確にしています。

  • S が匿名フィールド *T を含む場合:

    • S および *S の両方のメソッドセットは、レシーバが T または *T であるプロモートされたメソッドを含む。

    匿名フィールドが既にポインタ型 (*T) であるため、そのメソッドセットは T*T の両方のレシーバを持つメソッドを含みます。この場合、外側の構造体 S*S の両方が、この匿名ポインタフィールドのすべてのメソッドをプロモートするという、よりシンプルな規則になります。

これらの変更により、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 May 24, 2012",
+\t"Subtitle": "Version of June 4, 2012",
  	"Path": "/ref/spec"\n }-->
  
 @@ -684,6 +684,8 @@ consists of all methods with receiver type <code>T</code>.\n  The method set of the corresponding pointer type <code>*T</code>\n  is the set of all methods with receiver <code>*T</code> or <code>T</code>\n  (that is, it also contains the method set of <code>T</code>).\n+Further rules apply to structs containing anonymous fields, as described\n+in the section on <a href="#Struct_types">struct types</a>.\n  Any other type has an empty method set.\n  In a method set, each method must have a\n  <a href="#Uniqueness_of_identifiers">unique</a> <a href="#MethodName">method name</a>.\n@@ -955,28 +957,39 @@ struct {\n </pre>\n \n <p>\n-Fields and methods (§<a href="#Method_declarations">Method declarations</a>) of an anonymous field are\n-promoted to be ordinary fields and methods of the struct (§<a href="#Selectors">Selectors</a>).\n-The following rules apply for a struct type <code>S</code> and\n-a type named <code>T</code>:\n+A field or <a href="#Method_declarations">method</a> <code>f</code> of an\n+anonymous field in a struct <code>x</code> is called <i>promoted</i> if\n+<code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes\n+that field or method <code>f</code>.\n </p>\n-<ul>\n-\t<li>If <code>S</code> contains an anonymous field <code>T</code>, the\n-\t    <a href="#Method_sets">method set</a> of <code>S</code> includes the\n-\t    method set of <code>T</code>.\n-\t</li>\n \n-\t<li>If <code>S</code> contains an anonymous field <code>*T</code>, the\n-\t    method set of <code>S</code> includes the method set of <code>*T</code>\n-\t    (which itself includes the method set of <code>T</code>).\n-\t</li>\n+<p>\n+Promoted fields act like ordinary fields\n+of a struct except that they cannot be used as field names in\n+<a href="#Composite_literals">composite literals</a> of the struct.\n+</p>\n \n-\t<li>If <code>S</code> contains an anonymous field <code>T</code> or\n-\t    <code>*T</code>, the method set of <code>*S</code> includes the\n-\t    method set of <code>*T</code> (which itself includes the method\n-\t    set of <code>T</code>).\n+<p>\n+Given a struct type <code>S</code> and a type named <code>T</code>,\n+promoted methods are included in the method set of the struct as follows:\n+</p>\n+<ul>\n+\t<li>\n+\tIf <code>S</code> contains an anonymous field <code>T</code>,\n+\tthe <a href="#Method_sets">method sets</a> of <code>S</code>\n+\tand <code>*S</code> both include promoted methods with receiver\n+\t<code>T</code>. The method set of <code>*S</code> also\n+\tincludes promoted methods with receiver <code>*T</code>.\n+\t</li>\n+\t\n+\t<li>\n+\tIf <code>S</code> contains an anonymous field <code>*T</code>,\n+\tthe method sets of <code>S</code> and <code>*S</code> both\n+\tinclude promoted methods with receiver <code>T</code> or\n+\t<code>*T</code>.\n \t</li>\n </ul>\n+\n <p>\n  A field declaration may be followed by an optional string literal <i>tag</i>,\n  which becomes an attribute for all the fields in the corresponding\n```

## コアとなるコードの解説

このコミットの主要な変更は、Go言語の仕様書 `doc/go_spec.html` の「Struct types」セクションにおける匿名フィールドのプロモーション規則の記述です。

1.  **仕様書のバージョン日付の更新**:
    `Subtitle` のバージョン日付が "May 24, 2012" から "June 4, 2012" に更新されています。これは、このコミットが仕様書に対する正式な更新であることを示します。

2.  **メソッドセットに関する注釈の追加**:
    メソッドセットの定義に関する既存の段落に、「匿名フィールドを含む構造体には、構造体型に関するセクションで説明されている追加の規則が適用される」という注釈が追加されました。これにより、読者が匿名フィールドのプロモーション規則の重要性に気づき、関連セクションを参照するように促されます。

3.  **「プロモートされた」の定義の明確化**:
    変更前は、「匿名フィールドのフィールドとメソッドは、構造体の通常のフィールドとメソッドとしてプロモートされる」という一般的な記述でした。
    変更後は、以下の新しい段落が追加され、「プロモートされた」という用語がより厳密に定義されました。
    ```html
    <p>
    A field or <a href="#Method_declarations">method</a> <code>f</code> of an
    anonymous field in a struct <code>x</code> is called <i>promoted</i> if
    <code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes
    that field or method <code>f</code>.
    </p>
    ```
    これは、プロモーションがセレクタによるアクセス可能性に直接関連していることを明確にしています。

4.  **プロモートされたフィールドの複合リテラルに関する制約の追加**:
    以下の新しい段落が追加され、プロモートされたフィールドが構造体の複合リテラルにおいてフィールド名として使用できないという重要な制約が明記されました。
    ```html
    <p>
    Promoted fields act like ordinary fields
    of a struct except that they cannot be used as field names in
    <a href="#Composite_literals">composite literals</a> of the struct.
    </p>
    ```

5.  **メソッドセットへのプロモーション規則の精緻化**:
    最も重要な変更は、匿名フィールドのメソッドが構造体 `S` とそのポインタ型 `*S` のメソッドセットにどのように含まれるかに関する規則の書き換えです。

    **変更前の規則(削除された部分):**
    ```html
    <ul>
    	<li>If <code>S</code> contains an anonymous field <code>T</code>, the
    	    <a href="#Method_sets">method set</a> of <code>S</code> includes the
    	    method set of <code>T</code>.
    	</li>

    	<li>If <code>S</code> contains an anonymous field <code>*T</code>, the
    	    method set of <code>S</code> includes the method set of <code>*T</code>
    	    (which itself includes the method set of <code>T</code>).
    	</li>

    	<li>If <code>S</code> contains an anonymous field <code>T</code> or
    	    <code>*T</code>, the method set of <code>*S</code> includes the
    	    method set of <code>*T</code> (which itself includes the method
    	    set of <code>T</code>).
    	</li>
    </ul>
    ```

    **変更後の規則(追加された部分):**
    ```html
    <p>
    Given a struct type <code>S</code> and a type named <code>T</code>,
    promoted methods are included in the method set of the struct as follows:
    </p>
    <ul>
    	<li>
    	If <code>S</code> contains an anonymous field <code>T</code>,
    	the <a href="#Method_sets">method sets</a> of <code>S</code>
    	and <code>*S</code> both include promoted methods with receiver
    	<code>T</code>. The method set of <code>*S</code> also
    	includes promoted methods with receiver <code>*T</code>.
    	</li>
    	
    	<li>
    	If <code>S</code> contains an anonymous field <code>*T</code>,
    	the method sets of <code>S</code> and <code>*S</code> both
    	include promoted methods with receiver <code>T</code> or
    	<code>*T</code>.
    	</li>
    </ul>
    ```
    この新しい記述は、匿名フィールドの型(値型 `T` かポインタ型 `*T` か)と、外側の構造体の型(値型 `S` かポインタ型 `*S` か)の組み合わせに応じて、メソッドがどのようにプロモートされ、メソッドセットに含まれるかをより詳細かつ正確に説明しています。特に、`S` が匿名フィールド `T` を含む場合に、`*S` のメソッドセットが `*T` のレシーバを持つメソッドも含むことが明示された点が重要です。

これらの変更は、Go言語の型システム、特に匿名フィールドとメソッドセットの相互作用に関する理解を深める上で非常に価値があります。

## 関連リンク

- **Go言語仕様書 (現在のバージョン)**: [https://go.dev/ref/spec](https://go.dev/ref/spec)
- **Go言語の匿名フィールドに関する公式ブログ記事 (例)**: 該当する特定の記事はコミット時点では見つけられませんでしたが、Goのブログにはコンポジションに関する記事が多数あります。
- **Go言語のIssue #3635**: [https://github.com/golang/go/issues/3635](https://github.com/golang/go/issues/3635)

## 参考にした情報源リンク

- **Go言語仕様書**: このコミットの変更対象であるため、最も直接的な情報源です。
- **Go言語の公式ドキュメント**: 匿名フィールド、構造体、メソッドセットに関する一般的な情報。
- **Go言語のGitHubリポジトリ**: コミット履歴とIssueトラッカー。